From 684ae6554d9597ff027fa8e5ca797da1fa052375 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Tue, 6 Dec 2022 13:11:27 +0000 Subject: [PATCH 01/33] Create a placeholder for WASI threads implementation (#1783) This a simpler version of the PR: https://github.com/bytecodealliance/wasm-micro-runtime/pull/1638 --- CMakeLists.txt | 5 ++ build-scripts/runtime_lib.cmake | 8 ++ core/config.h | 4 + core/iwasm/common/wasm_native.c | 17 +++- .../lib-wasi-threads/lib_wasi_threads.cmake | 11 +++ .../lib_wasi_threads_wrapper.c | 35 ++++++++ product-mini/platforms/linux/CMakeLists.txt | 6 ++ samples/wasi-threads/CMakeLists.txt | 87 +++++++++++++++++++ samples/wasi-threads/README.md | 1 + samples/wasi-threads/wasm-apps/CMakeLists.txt | 29 +++++++ samples/wasi-threads/wasm-apps/no_pthread.c | 55 ++++++++++++ 11 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake create mode 100644 core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c create mode 100644 samples/wasi-threads/CMakeLists.txt create mode 100644 samples/wasi-threads/README.md create mode 100644 samples/wasi-threads/wasm-apps/CMakeLists.txt create mode 100644 samples/wasi-threads/wasm-apps/no_pthread.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f683868f2..d6466ba5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,11 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads library by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif () + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index 439a7c638..b6e6cd06b 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -124,6 +124,13 @@ if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) set (WAMR_BUILD_SHARED_MEMORY 1) endif () +if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) + include (${IWASM_DIR}/libraries/lib-wasi-threads/lib_wasi_threads.cmake) + # Enable the dependent feature if lib wasi threads is enabled + set (WAMR_BUILD_BULK_MEMORY 1) + set (WAMR_BUILD_SHARED_MEMORY 1) +endif () + if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) set (WAMR_BUILD_THREAD_MGR 1) include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake) @@ -186,6 +193,7 @@ set (source_all ${WASM_APP_LIB_SOURCE_ALL} ${NATIVE_INTERFACE_SOURCE} ${APP_MGR_SOURCE} + ${LIB_WASI_THREADS_SOURCE} ${LIB_PTHREAD_SOURCE} ${THREAD_MGR_SOURCE} ${LIBC_EMCC_SOURCE} diff --git a/core/config.h b/core/config.h index 5d6438384..2b6846e4f 100644 --- a/core/config.h +++ b/core/config.h @@ -161,6 +161,10 @@ #define WASM_ENABLE_LIB_PTHREAD_SEMAPHORE 0 #endif +#ifndef WASM_ENABLE_LIB_WASI_THREADS +#define WASM_ENABLE_LIB_WASI_THREADS 0 +#endif + #ifndef WASM_ENABLE_BASE_LIB #define WASM_ENABLE_BASE_LIB 0 #endif diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index 0fa596277..8c324cdc8 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -53,6 +53,11 @@ uint32 get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis); #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 +uint32 +get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis); +#endif + uint32 get_libc_emcc_export_apis(NativeSymbol **p_libc_emcc_apis); @@ -383,7 +388,7 @@ wasm_native_init() || WASM_ENABLE_BASE_LIB != 0 || WASM_ENABLE_LIBC_EMCC != 0 \ || WASM_ENABLE_LIB_RATS != 0 || WASM_ENABLE_WASI_NN != 0 \ || WASM_ENABLE_APP_FRAMEWORK != 0 || WASM_ENABLE_LIBC_WASI != 0 \ - || WASM_ENABLE_LIB_PTHREAD != 0 + || WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 NativeSymbol *native_symbols; uint32 n_native_symbols; #endif @@ -438,6 +443,14 @@ wasm_native_init() goto fail; #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + n_native_symbols = get_lib_wasi_threads_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("wasi", native_symbols, + n_native_symbols)) + goto fail; +#endif + #if WASM_ENABLE_LIBC_EMCC != 0 n_native_symbols = get_libc_emcc_export_apis(&native_symbols); if (n_native_symbols > 0 @@ -466,7 +479,7 @@ wasm_native_init() || WASM_ENABLE_BASE_LIB != 0 || WASM_ENABLE_LIBC_EMCC != 0 \ || WASM_ENABLE_LIB_RATS != 0 || WASM_ENABLE_WASI_NN != 0 \ || WASM_ENABLE_APP_FRAMEWORK != 0 || WASM_ENABLE_LIBC_WASI != 0 \ - || WASM_ENABLE_LIB_PTHREAD != 0 + || WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 fail: wasm_native_destroy(); return false; diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake new file mode 100644 index 000000000..2f300be52 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake @@ -0,0 +1,11 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_WASI_THREADS_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIB_WASI_THREADS=1) + +include_directories(${LIB_WASI_THREADS_DIR}) + +set (LIB_WASI_THREADS_SOURCE + ${LIB_WASI_THREADS_DIR}/lib_wasi_threads_wrapper.c) \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c new file mode 100644 index 000000000..4412a5bed --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasmtime_ssp.h" + +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif + +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +static __wasi_errno_t +thread_spawn_wrapper(wasm_exec_env_t exec_env, void *start_arg) +{ + return __WASI_ENOSYS; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_lib_wasi_threads[] = { REG_NATIVE_FUNC( + thread_spawn, "(*)i") }; + +uint32 +get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis) +{ + *p_lib_wasi_threads_apis = native_symbols_lib_wasi_threads; + return sizeof(native_symbols_lib_wasi_threads) / sizeof(NativeSymbol); +} diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index d3285be19..7f8463085 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -85,6 +85,12 @@ if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) set (WAMR_BUILD_LIB_PTHREAD 0) endif () +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads library by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif() + + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) diff --git a/samples/wasi-threads/CMakeLists.txt b/samples/wasi-threads/CMakeLists.txt new file mode 100644 index 000000000..93f6b3310 --- /dev/null +++ b/samples/wasi-threads/CMakeLists.txt @@ -0,0 +1,87 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(wasi_threads_sample) + +if (NOT DEFINED WASI_SYSROOT) + message (WARNING "Custom sysroot with threads enabled is required to build wasi threads samples. +Please note that current wasi-sdk doesn't ship with threads enabled. +Run cmake command with -DWASI_SYSROOT=/path/to/sysroot/with/threads to compile samples.") + return () +endif () + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_LIB_WASI_THREADS 1) + +# compiling and linking flags +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + + +################ wasm application ################ +add_subdirectory(wasm-apps) + +################ wamr runtime ################ +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${CMAKE_CURRENT_LIST_DIR}/../../product-mini/platforms/linux/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (iwasm ${RUNTIME_SOURCE_ALL}) +check_pie_supported() +set_target_properties (iwasm PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(iwasm vmlib -lpthread -lm -ldl) diff --git a/samples/wasi-threads/README.md b/samples/wasi-threads/README.md new file mode 100644 index 000000000..d7450af93 --- /dev/null +++ b/samples/wasi-threads/README.md @@ -0,0 +1 @@ +# "WASI threads" sample introduction \ No newline at end of file diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt new file mode 100644 index 000000000..9e3bef5a2 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_SYSROOT "${WASI_SYSROOT}") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") +set (CMAKE_C_COMPILER_TARGET "wasm32-wasi") + +function (compile_sample SOURCE_FILE) + get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) + set (WASM_MODULE ${FILE_NAME}.wasm) + add_executable (${WASM_MODULE} ${SOURCE_FILE}) + + target_compile_options (${WASM_MODULE} PRIVATE + -pthread -ftls-model=local-exec) + + target_link_options (${WASM_MODULE} PRIVATE + -z stack-size=32768 + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--shared-memory,--max-memory=1966080 + LINKER:--export=wasi_thread_start + ) +endfunction () + +compile_sample(no_pthread.c) diff --git a/samples/wasi-threads/wasm-apps/no_pthread.c b/samples/wasi-threads/wasm-apps/no_pthread.c new file mode 100644 index 000000000..3de2a9604 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/no_pthread.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include + +static const int64_t SECOND = 1000 * 1000 * 1000; + +typedef struct { + int th_ready; + int value; +} shared_t; + +__attribute__((export_name("wasi_thread_start"))) void +wasi_thread_start(int thread_id, int *start_arg) +{ + shared_t *data = (shared_t *)start_arg; + + printf("New thread ID: %d, starting parameter: %d\n", thread_id, + data->value); + + data->value += 8; + printf("Updated value: %d\n", data->value); + + data->th_ready = 1; + __builtin_wasm_memory_atomic_notify(&data->th_ready, 1); +} + +int +main(int argc, char **argv) +{ + shared_t data = { 0, 52 }; + __wasi_errno_t err; + + err = __wasi_thread_spawn(&data); + if (err != __WASI_ERRNO_SUCCESS) { + printf("Failed to create thread: %d\n", err); + return EXIT_FAILURE; + } + + if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) { + printf("Timeout\n"); + return EXIT_FAILURE; + } + + printf("Thread completed, new value: %d\n", data.value); + + return EXIT_SUCCESS; +} From 929d5942b9930ed001f5c159fddbdb01f84f16db Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Tue, 13 Dec 2022 13:33:27 +0000 Subject: [PATCH 02/33] Add implementation for wasi_thread_spawn() (#1786) For now this implementation uses thread manager. Not sure whether thread manager is needed in that case. In the future there'll be likely another syscall added (for pthread_exit) and for that we might need some kind of thread management - with that in mind, we keep thread manager for now and will refactor this later if needed. --- build-scripts/runtime_lib.cmake | 1 + core/iwasm/common/wasm_native.c | 15 +- .../lib_wasi_threads_wrapper.c | 143 +++++++++++++++++- samples/wasi-threads/wasm-apps/no_pthread.c | 18 ++- 4 files changed, 166 insertions(+), 11 deletions(-) diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index b6e6cd06b..973318866 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -127,6 +127,7 @@ endif () if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) include (${IWASM_DIR}/libraries/lib-wasi-threads/lib_wasi_threads.cmake) # Enable the dependent feature if lib wasi threads is enabled + set (WAMR_BUILD_THREAD_MGR 1) set (WAMR_BUILD_BULK_MEMORY 1) set (WAMR_BUILD_SHARED_MEMORY 1) endif () diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index 8c324cdc8..e80d4e78f 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -54,6 +54,12 @@ get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis); #endif #if WASM_ENABLE_LIB_WASI_THREADS != 0 +bool +lib_wasi_threads_init(void); + +void +lib_wasi_threads_destroy(void); + uint32 get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis); #endif @@ -444,6 +450,9 @@ wasm_native_init() #endif #if WASM_ENABLE_LIB_WASI_THREADS != 0 + if (!lib_wasi_threads_init()) + goto fail; + n_native_symbols = get_lib_wasi_threads_export_apis(&native_symbols); if (n_native_symbols > 0 && !wasm_native_register_natives("wasi", native_symbols, @@ -471,7 +480,7 @@ wasm_native_init() n_native_symbols = get_wasi_nn_export_apis(&native_symbols); if (!wasm_native_register_natives("wasi_nn", native_symbols, n_native_symbols)) - return false; + goto fail; #endif return true; @@ -495,6 +504,10 @@ wasm_native_destroy() lib_pthread_destroy(); #endif +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + lib_wasi_threads_destroy(); +#endif + node = g_native_symbols_list; while (node) { node_next = node->next; diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 4412a5bed..766aeb3cf 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#include "bh_log.h" #include "wasmtime_ssp.h" +#include "thread_manager.h" #if WASM_ENABLE_INTERP != 0 #include "wasm_runtime.h" @@ -13,10 +15,128 @@ #include "aot_runtime.h" #endif -static __wasi_errno_t -thread_spawn_wrapper(wasm_exec_env_t exec_env, void *start_arg) +static const char *THREAD_START_FUNCTION = "wasi_thread_start"; + +static korp_mutex thread_id_lock; + +typedef struct { + /* app's entry function */ + wasm_function_inst_t start_func; + /* arg of the app's entry function */ + uint32 arg; + /* thread id passed to the app */ + int32 thread_id; +} ThreadStartArg; + +static int32 +allocate_thread_id() { - return __WASI_ENOSYS; + static int32 thread_id = 0; + + int32 id; + + os_mutex_lock(&thread_id_lock); + id = thread_id++; + os_mutex_unlock(&thread_id_lock); + return id; +} + +static void * +thread_start(void *arg) +{ + wasm_exec_env_t exec_env = (wasm_exec_env_t)arg; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + ThreadStartArg *thread_arg = exec_env->thread_arg; + uint32 argv[2]; + + wasm_exec_env_set_thread_info(exec_env); + argv[0] = thread_arg->thread_id; + argv[1] = thread_arg->arg; + + if (!wasm_runtime_call_wasm(exec_env, thread_arg->start_func, 2, argv)) { + if (wasm_runtime_get_exception(module_inst)) + wasm_cluster_spread_exception(exec_env); + } + + /* routine exit, destroy instance */ + wasm_runtime_deinstantiate_internal(module_inst, true); + + wasm_runtime_free(thread_arg); + exec_env->thread_arg = NULL; + + return NULL; +} + +static int32 +thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg) +{ + wasm_module_t module = wasm_exec_env_get_module(exec_env); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_module_inst_t new_module_inst = NULL; + ThreadStartArg *thread_start_arg = NULL; + wasm_function_inst_t start_func; + int32 thread_id; + uint32 stack_size = 8192; + int32 ret = -1; +#if WASM_ENABLE_LIBC_WASI != 0 + WASIContext *wasi_ctx; +#endif + + bh_assert(module); + bh_assert(module_inst); + + stack_size = ((WASMModuleInstance *)module_inst)->default_wasm_stack_size; + + if (!(new_module_inst = wasm_runtime_instantiate_internal( + module, true, stack_size, 0, NULL, 0))) + return -1; + + wasm_runtime_set_custom_data_internal( + new_module_inst, wasm_runtime_get_custom_data(module_inst)); + +#if WASM_ENABLE_LIBC_WASI != 0 + wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + if (wasi_ctx) + wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx); +#endif + + start_func = wasm_runtime_lookup_function(new_module_inst, + THREAD_START_FUNCTION, NULL); + if (!start_func) { + LOG_ERROR("Failed to find thread start function %s", + THREAD_START_FUNCTION); + goto thread_spawn_fail; + } + + if (!(thread_start_arg = wasm_runtime_malloc(sizeof(ThreadStartArg)))) { + LOG_ERROR("Runtime args allocation failed"); + goto thread_spawn_fail; + } + + thread_start_arg->thread_id = thread_id = allocate_thread_id(); + thread_start_arg->arg = start_arg; + thread_start_arg->start_func = start_func; + + os_mutex_lock(&exec_env->wait_lock); + ret = wasm_cluster_create_thread(exec_env, new_module_inst, thread_start, + thread_start_arg); + if (ret != 0) { + os_mutex_unlock(&exec_env->wait_lock); + LOG_ERROR("Failed to spawn a new thread"); + goto thread_spawn_fail; + } + os_mutex_unlock(&exec_env->wait_lock); + + return thread_id; + +thread_spawn_fail: + if (new_module_inst) + wasm_runtime_deinstantiate_internal(new_module_inst, true); + + if (thread_start_arg) + wasm_runtime_free(thread_start_arg); + + return -1; } /* clang-format off */ @@ -25,7 +145,7 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, void *start_arg) /* clang-format on */ static NativeSymbol native_symbols_lib_wasi_threads[] = { REG_NATIVE_FUNC( - thread_spawn, "(*)i") }; + thread_spawn, "(i)i") }; uint32 get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis) @@ -33,3 +153,18 @@ get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis) *p_lib_wasi_threads_apis = native_symbols_lib_wasi_threads; return sizeof(native_symbols_lib_wasi_threads) / sizeof(NativeSymbol); } + +bool +lib_wasi_threads_init(void) +{ + if (0 != os_mutex_init(&thread_id_lock)) + return false; + + return true; +} + +void +lib_wasi_threads_destroy(void) +{ + os_mutex_destroy(&thread_id_lock); +} diff --git a/samples/wasi-threads/wasm-apps/no_pthread.c b/samples/wasi-threads/wasm-apps/no_pthread.c index 3de2a9604..6eb605004 100644 --- a/samples/wasi-threads/wasm-apps/no_pthread.c +++ b/samples/wasi-threads/wasm-apps/no_pthread.c @@ -8,6 +8,7 @@ #include #include +#include #include static const int64_t SECOND = 1000 * 1000 * 1000; @@ -15,6 +16,7 @@ static const int64_t SECOND = 1000 * 1000 * 1000; typedef struct { int th_ready; int value; + int thread_id; } shared_t; __attribute__((export_name("wasi_thread_start"))) void @@ -25,6 +27,7 @@ wasi_thread_start(int thread_id, int *start_arg) printf("New thread ID: %d, starting parameter: %d\n", thread_id, data->value); + data->thread_id = thread_id; data->value += 8; printf("Updated value: %d\n", data->value); @@ -35,12 +38,12 @@ wasi_thread_start(int thread_id, int *start_arg) int main(int argc, char **argv) { - shared_t data = { 0, 52 }; - __wasi_errno_t err; + shared_t data = { 0, 52, -1 }; + int thread_id; - err = __wasi_thread_spawn(&data); - if (err != __WASI_ERRNO_SUCCESS) { - printf("Failed to create thread: %d\n", err); + thread_id = __wasi_thread_spawn(&data); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; } @@ -49,7 +52,10 @@ main(int argc, char **argv) return EXIT_FAILURE; } - printf("Thread completed, new value: %d\n", data.value); + printf("Thread completed, new value: %d, thread id: %d\n", data.value, + data.thread_id); + + assert(thread_id == data.thread_id); return EXIT_SUCCESS; } From 8fc641377a1a590b70501f523d253f7a46654fbc Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Fri, 16 Dec 2022 06:22:53 +0000 Subject: [PATCH 03/33] feat(wasi-threads): Add example of exception propagation (#1812) --- samples/wasi-threads/README.md | 25 +++++- samples/wasi-threads/wasm-apps/CMakeLists.txt | 7 ++ .../wasm-apps/exception_propagation.c | 87 +++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 samples/wasi-threads/wasm-apps/exception_propagation.c diff --git a/samples/wasi-threads/README.md b/samples/wasi-threads/README.md index d7450af93..533f687ea 100644 --- a/samples/wasi-threads/README.md +++ b/samples/wasi-threads/README.md @@ -1 +1,24 @@ -# "WASI threads" sample introduction \ No newline at end of file +# "WASI threads" sample introduction + +Currently, since the `wasi-sdk` does not have thread support in the latest release, make sure to have [wasi-libc](https://github.com/WebAssembly/wasi-libc) installed. Build it with threads enabled, e.g. + +```shell +make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix +``` + +Build and run the samples + +```shell +$ mkdir build +$ cd build +$ cmake -DWASI_SYSROOT=/path/to/wasi-libc/sysroot .. +$ make +... +$ ./iwasm wasm-apps/no_pthread.wasm +... +$ ./iwasm wasm-apps/exception_propagation.wasm +``` diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt index 9e3bef5a2..25dba40b8 100644 --- a/samples/wasi-threads/wasm-apps/CMakeLists.txt +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -1,6 +1,12 @@ # Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () + if (NOT DEFINED WASI_SDK_DIR) set (WASI_SDK_DIR "/opt/wasi-sdk") endif () @@ -27,3 +33,4 @@ function (compile_sample SOURCE_FILE) endfunction () compile_sample(no_pthread.c) +compile_sample(exception_propagation.c) diff --git a/samples/wasi-threads/wasm-apps/exception_propagation.c b/samples/wasi-threads/wasm-apps/exception_propagation.c new file mode 100644 index 000000000..91f9f3560 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/exception_propagation.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __wasi__ +#error This example only compiles to WASM/WASI target +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_SECONDS 10 +#define NUM_THREADS 3 +static sem_t sem; + +void +run_long_task() +{ + // Busy waiting to be interruptible by exception + for (int i = 0; i < TIMEOUT_SECONDS; i++) + sleep(1); +} + +__attribute__((export_name("wasi_thread_start"))) void +wasi_thread_start(int thread_id, int *start_arg) +{ + bool has_to_throw_exception = (bool)start_arg; + + if (has_to_throw_exception) { + // Wait for all other threads (including main thread) to be ready + printf("Waiting before throwing exception\n"); + for (int i = 0; i < NUM_THREADS; i++) + sem_wait(&sem); + + printf("Throwing exception\n"); + __builtin_trap(); + } + else { + printf("Thread running\n"); + + sem_post(&sem); + run_long_task(); // Wait to be interrupted by exception + assert(false && "Unreachable"); + } +} + +int +main(int argc, char **argv) +{ + int thread_id = -1; + if (sem_init(&sem, 0, 0) != 0) { + printf("Failed to init semaphore\n"); + return EXIT_FAILURE; + } + + // Create a thread that throws an exception + thread_id = __wasi_thread_spawn((void *)true); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + + // Create two additional threads to test exception propagation + thread_id = __wasi_thread_spawn((void *)false); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + thread_id = __wasi_thread_spawn((void *)false); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + + printf("Main thread running\n"); + + sem_post(&sem); + run_long_task(); // Wait to be interrupted by exception + assert(false && "Unreachable"); + + return EXIT_SUCCESS; +} \ No newline at end of file From 0f637cff0727a70022ea28cfb758a7090b51511d Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Mon, 19 Dec 2022 02:58:12 +0000 Subject: [PATCH 04/33] Enable aux stack allocations on application heap (#1799) This is necessary for WASI threads as the aux stack should be managed by the application. See https://github.com/bytecodealliance/wasm-micro-runtime/issues/1790 for details. --- core/config.h | 7 ++ core/iwasm/interpreter/wasm_runtime.c | 6 +- .../lib-pthread/lib_pthread_wrapper.c | 4 -- .../lib-wasi-threads/lib_wasi_threads.cmake | 2 +- .../lib_wasi_threads_wrapper.c | 4 -- .../libraries/thread-mgr/thread_manager.c | 69 +++++++++++++++---- .../libraries/thread-mgr/thread_manager.h | 6 +- 7 files changed, 72 insertions(+), 26 deletions(-) diff --git a/core/config.h b/core/config.h index 2b6846e4f..feedb13fa 100644 --- a/core/config.h +++ b/core/config.h @@ -165,6 +165,13 @@ #define WASM_ENABLE_LIB_WASI_THREADS 0 #endif +#ifndef WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION +#define WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION WASM_ENABLE_LIB_WASI_THREADS +#elif WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 \ + && WASM_ENABLE_LIB_WASI_THREADS == 1 +#error "Heap aux stack allocation must be enabled for WASI threads" +#endif + #ifndef WASM_ENABLE_BASE_LIB #define WASM_ENABLE_BASE_LIB 0 #endif diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 8e8f96b5c..cda98c7c3 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -2443,14 +2443,16 @@ wasm_set_aux_stack(WASMExecEnv *exec_env, uint32 start_offset, uint32 size) WASMModuleInstance *module_inst = (WASMModuleInstance *)exec_env->module_inst; uint32 stack_top_idx = module_inst->module->aux_stack_top_global_index; + +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 + /* Check the aux stack space */ uint32 data_end = module_inst->module->aux_data_end; uint32 stack_bottom = module_inst->module->aux_stack_bottom; bool is_stack_before_data = stack_bottom < data_end ? true : false; - - /* Check the aux stack space, currently we don't allocate space in heap */ if ((is_stack_before_data && (size > start_offset)) || ((!is_stack_before_data) && (start_offset - data_end < size))) return false; +#endif if (stack_top_idx != (uint32)-1) { /* The aux stack top is a wasm global, diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 2fb7033f3..9f5a9a8f3 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -504,7 +504,6 @@ pthread_start_routine(void *arg) info_node->exec_env = exec_env; info_node->u.thread = exec_env->handle; if (!append_thread_info_node(info_node)) { - wasm_runtime_deinstantiate_internal(module_inst, true); delete_thread_info_node(info_node); os_cond_signal(&parent_exec_env->wait_cond); os_mutex_unlock(&parent_exec_env->wait_lock); @@ -527,9 +526,6 @@ pthread_start_routine(void *arg) /* destroy pthread key values */ call_key_destructor(exec_env); - /* routine exit, destroy instance */ - wasm_runtime_deinstantiate_internal(module_inst, true); - wasm_runtime_free(routine_args); /* if the thread is joinable, store the result in its info node, diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake index 2f300be52..49f87077f 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake @@ -3,7 +3,7 @@ set (LIB_WASI_THREADS_DIR ${CMAKE_CURRENT_LIST_DIR}) -add_definitions (-DWASM_ENABLE_LIB_WASI_THREADS=1) +add_definitions (-DWASM_ENABLE_LIB_WASI_THREADS=1 -DWASM_ENABLE_HEAP_AUX_STACK_ALLOCATION=1) include_directories(${LIB_WASI_THREADS_DIR}) diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 766aeb3cf..4eae941f6 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -4,7 +4,6 @@ */ #include "bh_log.h" -#include "wasmtime_ssp.h" #include "thread_manager.h" #if WASM_ENABLE_INTERP != 0 @@ -58,9 +57,6 @@ thread_start(void *arg) wasm_cluster_spread_exception(exec_env); } - /* routine exit, destroy instance */ - wasm_runtime_deinstantiate_internal(module_inst, true); - wasm_runtime_free(thread_arg); exec_env->thread_arg = NULL; diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 4408b0133..ede36ebe2 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -77,8 +77,18 @@ traverse_list(bh_list *l, list_visitor visitor, void *user_data) } static bool -allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size) +allocate_aux_stack(WASMExecEnv *exec_env, uint32 *start, uint32 *size) { + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + + *start = wasm_runtime_module_malloc(module_inst, cluster->stack_size, NULL); + *size = cluster->stack_size; + + return *start != 0; +#else uint32 i; /* If the module doesn't have aux stack info, @@ -100,11 +110,21 @@ allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size) } os_mutex_unlock(&cluster->lock); return false; +#endif } static bool -free_aux_stack(WASMCluster *cluster, uint32 start) +free_aux_stack(WASMExecEnv *exec_env, uint32 start) { +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + + wasm_runtime_module_free(module_inst, start); + + return true; +#else + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); uint32 i; for (i = 0; i < cluster_max_thread_num; i++) { @@ -116,14 +136,14 @@ free_aux_stack(WASMCluster *cluster, uint32 start) } } return false; +#endif } WASMCluster * wasm_cluster_create(WASMExecEnv *exec_env) { WASMCluster *cluster; - uint64 total_size; - uint32 aux_stack_start, aux_stack_size, i; + uint32 aux_stack_start, aux_stack_size; bh_assert(exec_env->cluster == NULL); if (!(cluster = wasm_runtime_malloc(sizeof(WASMCluster)))) { @@ -159,12 +179,16 @@ wasm_cluster_create(WASMExecEnv *exec_env) return cluster; } +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 + cluster->stack_size = aux_stack_size; +#else cluster->stack_size = aux_stack_size / (cluster_max_thread_num + 1); if (cluster->stack_size < WASM_THREAD_AUX_STACK_SIZE_MIN) { goto fail; } /* Make stack size 16-byte aligned */ cluster->stack_size = cluster->stack_size & (~15); +#endif /* Set initial aux stack top to the instance and aux stack boundary to the main exec_env */ @@ -172,8 +196,10 @@ wasm_cluster_create(WASMExecEnv *exec_env) cluster->stack_size)) goto fail; +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 if (cluster_max_thread_num != 0) { - total_size = cluster_max_thread_num * sizeof(uint32); + uint64 total_size = cluster_max_thread_num * sizeof(uint32); + uint32 i; if (total_size >= UINT32_MAX || !(cluster->stack_tops = wasm_runtime_malloc((uint32)total_size))) { @@ -195,6 +221,7 @@ wasm_cluster_create(WASMExecEnv *exec_env) cluster->stack_tops[i] = aux_stack_start - cluster->stack_size * i; } } +#endif os_mutex_lock(&cluster_list_lock); if (bh_list_insert(cluster_list, cluster) != 0) { @@ -234,10 +261,12 @@ wasm_cluster_destroy(WASMCluster *cluster) os_mutex_destroy(&cluster->lock); +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 if (cluster->stack_tops) wasm_runtime_free(cluster->stack_tops); if (cluster->stack_segment_occupied) wasm_runtime_free(cluster->stack_segment_occupied); +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 wasm_debug_instance_destroy(cluster); @@ -273,7 +302,13 @@ wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) exec_env->cluster = cluster; os_mutex_lock(&cluster->lock); - if (bh_list_insert(&cluster->exec_env_list, exec_env) != 0) + if (cluster->exec_env_list.len == cluster_max_thread_num + 1) { + LOG_ERROR("thread manager error: " + "maximum number of threads exceeded"); + ret = false; + } + + if (ret && bh_list_insert(&cluster->exec_env_list, exec_env) != 0) ret = false; os_mutex_unlock(&cluster->lock); return ret; @@ -407,7 +442,7 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) if (!new_exec_env) goto fail1; - if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) { + if (!allocate_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { LOG_ERROR("thread manager error: " "failed to allocate aux stack space for new thread"); goto fail2; @@ -426,7 +461,7 @@ wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) fail3: /* free the allocated aux stack space */ - free_aux_stack(cluster, aux_stack_start); + free_aux_stack(exec_env, aux_stack_start); fail2: wasm_exec_env_destroy(new_exec_env); fail1: @@ -443,7 +478,7 @@ wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) bh_assert(cluster != NULL); /* Free aux stack space */ - free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom); + free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); wasm_cluster_del_exec_env(cluster, exec_env); wasm_exec_env_destroy_internal(exec_env); @@ -457,7 +492,11 @@ thread_manager_start_routine(void *arg) void *ret; WASMExecEnv *exec_env = (WASMExecEnv *)arg; WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + bh_assert(cluster != NULL); + bh_assert(module_inst != NULL); exec_env->handle = os_self_thread(); ret = exec_env->thread_start_routine(exec_env); @@ -469,7 +508,11 @@ thread_manager_start_routine(void *arg) /* Routine exit */ /* Free aux stack space */ - free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom); + free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); + + /* routine exit, destroy instance */ + wasm_runtime_deinstantiate_internal(module_inst, true); + /* Detach the native thread here to ensure the resources are freed */ wasm_cluster_detach_thread(exec_env); #if WASM_ENABLE_DEBUG_INTERP != 0 @@ -501,7 +544,7 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, if (!new_exec_env) return -1; - if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) { + if (!allocate_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { LOG_ERROR("thread manager error: " "failed to allocate aux stack space for new thread"); goto fail1; @@ -532,7 +575,7 @@ fail3: wasm_cluster_del_exec_env(cluster, new_exec_env); fail2: /* free the allocated aux stack space */ - free_aux_stack(cluster, aux_stack_start); + free_aux_stack(exec_env, aux_stack_start); fail1: wasm_exec_env_destroy(new_exec_env); return -1; @@ -762,7 +805,7 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) #endif /* App exit the thread, free the resources before exit native thread */ /* Free aux stack space */ - free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom); + free_aux_stack(exec_env, exec_env->aux_stack_bottom.bottom); /* Detach the native thread here to ensure the resources are freed */ wasm_cluster_detach_thread(exec_env); /* Remove and destroy exec_env */ diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h index c84d3c021..aa1a9154b 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.h +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -26,14 +26,16 @@ struct WASMCluster { korp_mutex lock; bh_list exec_env_list; +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 /* The aux stack of a module with shared memory will be divided into several segments. This array store the stack top of different segments */ uint32 *stack_tops; - /* Size of every stack segment */ - uint32 stack_size; /* Record which segments are occupied */ bool *stack_segment_occupied; +#endif + /* Size of every stack segment */ + uint32 stack_size; #if WASM_ENABLE_DEBUG_INTERP != 0 WASMDebugInstance *debug_inst; #endif From 0c85cb1fe6fea7da39f737909517af7c4e2826b5 Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Mon, 19 Dec 2022 11:55:01 +0000 Subject: [PATCH 05/33] feat(wasi-threads): Improve thread id allocator to reuse identifiers (#1809) This PR allows reusing thread ids once they are released. That is done by using a stack data structure to keep track of the used ids. When a thread is created, it takes an available identifier from the stack. When the thread exits, it returns the id to the stack of available identifiers. --- .../lib_wasi_threads_wrapper.c | 86 +++++++++++++++++-- 1 file changed, 78 insertions(+), 8 deletions(-) diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 4eae941f6..2fa90cee3 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -18,6 +18,14 @@ static const char *THREAD_START_FUNCTION = "wasi_thread_start"; static korp_mutex thread_id_lock; +// Stack data structure to track available thread identifiers +#define AVAIL_TIDS_INIT_SIZE CLUSTER_MAX_THREAD_NUM +typedef struct { + int32 *ids; + uint32 pos, size; +} AvailableThreadIds; +static AvailableThreadIds avail_tids; + typedef struct { /* app's entry function */ wasm_function_inst_t start_func; @@ -30,16 +38,56 @@ typedef struct { static int32 allocate_thread_id() { - static int32 thread_id = 0; - - int32 id; + int32 id = -1; os_mutex_lock(&thread_id_lock); - id = thread_id++; + if (avail_tids.pos == 0) { // Resize stack and push new thread ids + uint32 old_size = avail_tids.size; + uint32 new_size = avail_tids.size * 2; + if (new_size / 2 != avail_tids.size) { + LOG_ERROR("Overflow detected during new size calculation"); + goto return_id; + } + + size_t realloc_size = new_size * sizeof(int32); + if (realloc_size / sizeof(int32) != new_size) { + LOG_ERROR("Overflow detected during realloc"); + goto return_id; + } + int32 *tmp = + (int32 *)wasm_runtime_realloc(avail_tids.ids, realloc_size); + if (tmp == NULL) { + LOG_ERROR("Thread ID allocator realloc failed"); + goto return_id; + } + + avail_tids.size = new_size; + avail_tids.pos = old_size; + avail_tids.ids = tmp; + for (uint32 i = 0; i < old_size; i++) + avail_tids.ids[i] = new_size - i; + } + + // Pop available thread identifier from `avail_tids` stack + id = avail_tids.ids[--avail_tids.pos]; + +return_id: os_mutex_unlock(&thread_id_lock); return id; } +void +deallocate_thread_id(int32 thread_id) +{ + os_mutex_lock(&thread_id_lock); + + // Release thread identifier by pushing it into `avail_tids` stack + bh_assert(avail_tids.pos < avail_tids.size); + avail_tids.ids[avail_tids.pos++] = thread_id; + + os_mutex_unlock(&thread_id_lock); +} + static void * thread_start(void *arg) { @@ -57,6 +105,8 @@ thread_start(void *arg) wasm_cluster_spread_exception(exec_env); } + // Routine exit + deallocate_thread_id(thread_arg->thread_id); wasm_runtime_free(thread_arg); exec_env->thread_arg = NULL; @@ -101,15 +151,19 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg) if (!start_func) { LOG_ERROR("Failed to find thread start function %s", THREAD_START_FUNCTION); - goto thread_spawn_fail; + goto thread_preparation_fail; } if (!(thread_start_arg = wasm_runtime_malloc(sizeof(ThreadStartArg)))) { LOG_ERROR("Runtime args allocation failed"); - goto thread_spawn_fail; + goto thread_preparation_fail; } thread_start_arg->thread_id = thread_id = allocate_thread_id(); + if (thread_id < 0) { + LOG_ERROR("Failed to get thread identifier"); + goto thread_preparation_fail; + } thread_start_arg->arg = start_arg; thread_start_arg->start_func = start_func; @@ -117,7 +171,6 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg) ret = wasm_cluster_create_thread(exec_env, new_module_inst, thread_start, thread_start_arg); if (ret != 0) { - os_mutex_unlock(&exec_env->wait_lock); LOG_ERROR("Failed to spawn a new thread"); goto thread_spawn_fail; } @@ -126,9 +179,12 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg) return thread_id; thread_spawn_fail: + os_mutex_unlock(&exec_env->wait_lock); + deallocate_thread_id(thread_id); + +thread_preparation_fail: if (new_module_inst) wasm_runtime_deinstantiate_internal(new_module_inst, true); - if (thread_start_arg) wasm_runtime_free(thread_start_arg); @@ -156,11 +212,25 @@ lib_wasi_threads_init(void) if (0 != os_mutex_init(&thread_id_lock)) return false; + // Initialize stack to store thread identifiers + avail_tids.size = AVAIL_TIDS_INIT_SIZE; + avail_tids.pos = avail_tids.size; + avail_tids.ids = + (int32 *)wasm_runtime_malloc(avail_tids.size * sizeof(int32)); + if (avail_tids.ids == NULL) { + os_mutex_destroy(&thread_id_lock); + return false; + } + for (uint32 i = 0; i < avail_tids.size; i++) + avail_tids.ids[i] = avail_tids.size - i; + return true; } void lib_wasi_threads_destroy(void) { + wasm_runtime_free(avail_tids.ids); + avail_tids.ids = NULL; os_mutex_destroy(&thread_id_lock); } From 37e9b9c51050a418f59877966f34dff1c8f1fb26 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Wed, 7 Dec 2022 16:43:04 +0800 Subject: [PATCH 06/33] Fix wasm-c-api import func link issue in wasm_instance_new (#1787) When a wasm module is duplicated instantiated with wasm_instance_new, the function import info of the previous instantiation may be overwritten by the later instantiation, which may cause unexpected behavior. Store the function import info into the module instance to fix the issue. --- core/iwasm/aot/aot_runtime.c | 22 +++++- core/iwasm/aot/aot_runtime.h | 4 + core/iwasm/common/wasm_c_api.c | 80 ++++++++++++++++---- core/iwasm/interpreter/wasm.h | 1 - core/iwasm/interpreter/wasm_interp_classic.c | 11 ++- core/iwasm/interpreter/wasm_interp_fast.c | 11 ++- core/iwasm/interpreter/wasm_runtime.c | 11 ++- core/iwasm/interpreter/wasm_runtime.h | 12 +++ 8 files changed, 128 insertions(+), 24 deletions(-) diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 1077c3af8..133c5e38a 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -987,7 +987,7 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, (uint64)module->memory_count * sizeof(AOTMemoryInstance); uint64 total_size, table_size = 0; uint8 *p; - uint32 i; + uint32 i, extra_info_offset; /* Check heap size */ heap_size = align_uint(heap_size, 8); @@ -1015,6 +1015,11 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, } total_size += table_size; + /* The offset of AOTModuleInstanceExtra, make it 8-byte aligned */ + total_size = (total_size + 7LL) & ~7LL; + extra_info_offset = (uint32)total_size; + total_size += sizeof(AOTModuleInstanceExtra); + /* Allocate module instance, global data, table data and heap data */ if (!(module_inst = runtime_malloc(total_size, error_buf, error_buf_size))) { @@ -1023,6 +1028,8 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size, module_inst->module_type = Wasm_Module_AoT; module_inst->module = (void *)module; + module_inst->e = + (WASMModuleInstanceExtra *)((uint8 *)module_inst + extra_info_offset); /* Initialize global info */ p = (uint8 *)module_inst + module_inst_struct_size @@ -1179,6 +1186,10 @@ aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) if (module_inst->exec_env_singleton) wasm_exec_env_destroy((WASMExecEnv *)module_inst->exec_env_singleton); + if (((AOTModuleInstanceExtra *)module_inst->e)->c_api_func_imports) + wasm_runtime_free( + ((AOTModuleInstanceExtra *)module_inst->e)->c_api_func_imports); + wasm_runtime_free(module_inst); } @@ -1750,6 +1761,10 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, AOTModuleInstance *module_inst = (AOTModuleInstance *)wasm_runtime_get_module_inst(exec_env); AOTModule *aot_module = (AOTModule *)module_inst->module; + AOTModuleInstanceExtra *module_inst_extra = + (AOTModuleInstanceExtra *)module_inst->e; + CApiFuncImport *c_api_func_import = + module_inst_extra->c_api_func_imports + func_idx; uint32 *func_type_indexes = module_inst->func_type_indexes; uint32 func_type_idx = func_type_indexes[func_idx]; AOTFuncType *func_type = aot_module->func_types[func_type_idx]; @@ -1764,6 +1779,9 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, bh_assert(func_idx < aot_module->import_func_count); import_func = aot_module->import_funcs + func_idx; + if (import_func->call_conv_wasm_c_api) + func_ptr = c_api_func_import->func_ptr_linked; + if (!func_ptr) { snprintf(buf, sizeof(buf), "failed to call unlinked import function (%s, %s)", @@ -1776,7 +1794,7 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, if (import_func->call_conv_wasm_c_api) { ret = wasm_runtime_invoke_c_api_native( (WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc, - argv, import_func->wasm_c_api_with_env, attachment); + argv, c_api_func_import->with_env_arg, c_api_func_import->env_arg); } else if (!import_func->call_conv_raw) { signature = import_func->signature; diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index d02af3cb9..a63717299 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -73,6 +73,10 @@ typedef struct AOTFunctionInstance { } u; } AOTFunctionInstance; +typedef struct AOTModuleInstanceExtra { + CApiFuncImport *c_api_func_imports; +} AOTModuleInstanceExtra; + #if defined(OS_ENABLE_HW_BOUND_CHECK) && defined(BH_PLATFORM_WINDOWS) /* clang-format off */ typedef struct AOTUnwindInfo { diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index f6212d5de..619e02297 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -4282,15 +4282,13 @@ interp_link_func(const wasm_instance_t *inst, const WASMModule *module_interp, return false; imported_func_interp->u.function.call_conv_wasm_c_api = true; - imported_func_interp->u.function.wasm_c_api_with_env = import->with_env; - if (import->with_env) { + /* only set func_ptr_linked to avoid unlink warning during instantiation, + func_ptr_linked, with_env and env will be stored in module instance's + c_api_func_imports later and used when calling import function */ + if (import->with_env) imported_func_interp->u.function.func_ptr_linked = import->u.cb_env.cb; - imported_func_interp->u.function.attachment = import->u.cb_env.env; - } - else { + else imported_func_interp->u.function.func_ptr_linked = import->u.cb; - imported_func_interp->u.function.attachment = NULL; - } import->func_idx_rt = func_idx_rt; return true; @@ -4496,15 +4494,13 @@ aot_link_func(const wasm_instance_t *inst, const AOTModule *module_aot, return false; import_aot_func->call_conv_wasm_c_api = true; - import_aot_func->wasm_c_api_with_env = import->with_env; - if (import->with_env) { + /* only set func_ptr_linked to avoid unlink warning during instantiation, + func_ptr_linked, with_env and env will be stored in module instance's + c_api_func_imports later and used when calling import function */ + if (import->with_env) import_aot_func->func_ptr_linked = import->u.cb_env.cb; - import_aot_func->attachment = import->u.cb_env.env; - } - else { + else import_aot_func->func_ptr_linked = import->u.cb; - import_aot_func->attachment = NULL; - } import->func_idx_rt = import_func_idx_rt; return true; @@ -4718,10 +4714,12 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, { char sub_error_buf[128] = { 0 }; char error_buf[256] = { 0 }; - uint32 import_count = 0; bool import_count_verified = false; wasm_instance_t *instance = NULL; - uint32 i = 0; + WASMModuleInstance *inst_rt; + CApiFuncImport *func_import = NULL, **p_func_imports = NULL; + uint32 i = 0, import_count = 0, import_func_count = 0; + uint64 total_size; bool processed = false; bh_assert(singleton_engine); @@ -4804,6 +4802,56 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, goto failed; } + inst_rt = (WASMModuleInstance *)instance->inst_comm_rt; +#if WASM_ENABLE_INTERP != 0 + if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + p_func_imports = &inst_rt->e->c_api_func_imports; + import_func_count = inst_rt->module->import_function_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (instance->inst_comm_rt->module_type == Wasm_Module_AoT) { + p_func_imports = + &((AOTModuleInstanceExtra *)inst_rt->e)->c_api_func_imports; + import_func_count = ((AOTModule *)inst_rt->module)->import_func_count; + } +#endif + bh_assert(p_func_imports); + + /* create the c-api func import list */ + total_size = (uint64)sizeof(CApiFuncImport) * import_func_count; + if (total_size > 0 + && !(*p_func_imports = func_import = malloc_internal(total_size))) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Failed to create wasm-c-api func imports"); + goto failed; + } + + /* fill in c-api func import list */ + for (i = 0; i < import_count; i++) { + wasm_func_t *func_host; + wasm_extern_t *in; + + in = imports->data[i]; + if (wasm_extern_kind(in) != WASM_EXTERN_FUNC) + continue; + + func_host = wasm_extern_as_func(in); + + func_import->with_env_arg = func_host->with_env; + if (func_host->with_env) { + func_import->func_ptr_linked = func_host->u.cb_env.cb; + func_import->env_arg = func_host->u.cb_env.env; + } + else { + func_import->func_ptr_linked = func_host->u.cb; + func_import->env_arg = NULL; + } + + func_import++; + } + bh_assert((uint32)(func_import - *p_func_imports) == import_func_count); + /* fill with inst */ for (i = 0; imports && imports->data && i < (uint32)import_count; ++i) { wasm_extern_t *import = imports->data[i]; diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 9c6a2ce3d..58305a7f4 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -189,7 +189,6 @@ typedef struct WASMFunctionImport { WASMFunction *import_func_linked; #endif bool call_conv_wasm_c_api; - bool wasm_c_api_with_env; } WASMFunctionImport; typedef struct WASMGlobalImport { diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 45ef4d567..39c23f9fb 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -838,6 +838,7 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, WASMInterpFrame *prev_frame) { WASMFunctionImport *func_import = cur_func->u.func_import; + CApiFuncImport *c_api_func_import = NULL; unsigned local_cell_num = 2; WASMInterpFrame *frame; uint32 argv_ret[2], cur_func_index; @@ -858,7 +859,13 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, cur_func_index = (uint32)(cur_func - module_inst->e->functions); bh_assert(cur_func_index < module_inst->module->import_function_count); - native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; + if (!func_import->call_conv_wasm_c_api) { + native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; + } + else { + c_api_func_import = module_inst->e->c_api_func_imports + cur_func_index; + native_func_pointer = c_api_func_import->func_ptr_linked; + } if (!native_func_pointer) { snprintf(buf, sizeof(buf), @@ -872,7 +879,7 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, ret = wasm_runtime_invoke_c_api_native( (WASMModuleInstanceCommon *)module_inst, native_func_pointer, func_import->func_type, cur_func->param_cell_num, frame->lp, - func_import->wasm_c_api_with_env, func_import->attachment); + c_api_func_import->with_env_arg, c_api_func_import->env_arg); if (ret) { argv_ret[0] = frame->lp[0]; argv_ret[1] = frame->lp[1]; diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index df356d56f..3109b0c82 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -902,6 +902,7 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, WASMInterpFrame *prev_frame) { WASMFunctionImport *func_import = cur_func->u.func_import; + CApiFuncImport *c_api_func_import = NULL; unsigned local_cell_num = 2; WASMInterpFrame *frame; uint32 argv_ret[2], cur_func_index; @@ -921,7 +922,13 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, cur_func_index = (uint32)(cur_func - module_inst->e->functions); bh_assert(cur_func_index < module_inst->module->import_function_count); - native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; + if (!func_import->call_conv_wasm_c_api) { + native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; + } + else { + c_api_func_import = module_inst->e->c_api_func_imports + cur_func_index; + native_func_pointer = c_api_func_import->func_ptr_linked; + } if (!native_func_pointer) { char buf[128]; @@ -936,7 +943,7 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, ret = wasm_runtime_invoke_c_api_native( (WASMModuleInstanceCommon *)module_inst, native_func_pointer, func_import->func_type, cur_func->param_cell_num, frame->lp, - func_import->wasm_c_api_with_env, func_import->attachment); + c_api_func_import->with_env_arg, c_api_func_import->env_arg); if (ret) { argv_ret[0] = frame->lp[0]; argv_ret[1] = frame->lp[1]; diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index cda98c7c3..1da8a19ad 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -1948,6 +1948,9 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) os_mutex_destroy(&module_inst->e->mem_lock); #endif + if (module_inst->e->c_api_func_imports) + wasm_runtime_free(module_inst->e->c_api_func_imports); + wasm_runtime_free(module_inst); } @@ -2851,6 +2854,7 @@ llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, WASMType *func_type; void *func_ptr; WASMFunctionImport *import_func; + CApiFuncImport *c_api_func_import = NULL; const char *signature; void *attachment; char buf[96]; @@ -2872,6 +2876,11 @@ llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, bh_assert(func_idx < module->import_function_count); import_func = &module->import_functions[func_idx].u.function; + if (import_func->call_conv_wasm_c_api) { + c_api_func_import = module_inst->e->c_api_func_imports + func_idx; + func_ptr = c_api_func_import->func_ptr_linked; + } + if (!func_ptr) { snprintf(buf, sizeof(buf), "failed to call unlinked import function (%s, %s)", @@ -2884,7 +2893,7 @@ llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, if (import_func->call_conv_wasm_c_api) { ret = wasm_runtime_invoke_c_api_native( (WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc, - argv, import_func->wasm_c_api_with_env, attachment); + argv, c_api_func_import->with_env_arg, c_api_func_import->env_arg); } else if (!import_func->call_conv_raw) { signature = import_func->signature; diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 3146b18fd..1ebac5f3c 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -192,6 +192,16 @@ typedef struct WASMExportMemInstance { WASMMemoryInstance *memory; } WASMExportMemInstance; +/* wasm-c-api import function info */ +typedef struct CApiFuncImport { + /* host func pointer after linked */ + void *func_ptr_linked; + /* whether the host func has env argument */ + bool with_env_arg; + /* the env argument of the host func */ + void *env_arg; +} CApiFuncImport; + /* Extra info of WASM module instance for interpreter/jit mode */ typedef struct WASMModuleInstanceExtra { WASMGlobalInstance *globals; @@ -205,6 +215,8 @@ typedef struct WASMModuleInstanceExtra { WASMFunctionInstance *free_function; WASMFunctionInstance *retain_function; + CApiFuncImport *c_api_func_imports; + #if WASM_ENABLE_SHARED_MEMORY != 0 /* lock for shared memory atomic operations */ korp_mutex mem_lock; From ead6478285fd3b9aab4f2a4d5c3128c1d7be52ae Mon Sep 17 00:00:00 2001 From: Callum Macmillan Date: Wed, 7 Dec 2022 09:18:28 +0000 Subject: [PATCH 07/33] Add memory watchpoint support for source debugger (#1762) Allow to add watchpoints to variables for source debugging. For instance: `breakpoint set variable var` will pause WAMR execution when the address at var is written to. Can also set read/write watchpoints by passing r/w flags. This will pause execution when the address at var is read: `watchpoint set variable -w read var` Add two linked lists for read/write watchpoints. When the debug message handler receives a watchpoint request, it adds/removes to one/both of these lists. In the interpreter, when an address is read or stored to, check whether the address is in these lists. If so, throw a sigtrap and suspend the process. --- core/iwasm/interpreter/wasm_interp_classic.c | 49 ++++++- .../libraries/debug-engine/debug_engine.c | 82 +++++++++++ .../libraries/debug-engine/debug_engine.h | 24 ++++ core/iwasm/libraries/debug-engine/handler.c | 135 ++++++++++++++---- 4 files changed, 265 insertions(+), 25 deletions(-) diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 39c23f9fb..aa15b41a7 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -70,6 +70,35 @@ typedef float64 CellType_F64; goto unaligned_atomic; \ } while (0) +#if WASM_ENABLE_DEBUG_INTERP != 0 +#define TRIGGER_WATCHPOINT_SIGTRAP() \ + do { \ + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP); \ + CHECK_SUSPEND_FLAGS(); \ + } while (0) + +#define CHECK_WATCHPOINT(list, current_addr) \ + do { \ + WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list); \ + while (watchpoint) { \ + WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint); \ + if (watchpoint->addr <= current_addr \ + && watchpoint->addr + watchpoint->length > current_addr) { \ + TRIGGER_WATCHPOINT_SIGTRAP(); \ + } \ + watchpoint = next; \ + } \ + } while (0) + +#define CHECK_READ_WATCHPOINT(addr, offset) \ + CHECK_WATCHPOINT(watch_point_list_read, WASM_ADDR_OFFSET(addr + offset)) +#define CHECK_WRITE_WATCHPOINT(addr, offset) \ + CHECK_WATCHPOINT(watch_point_list_write, WASM_ADDR_OFFSET(addr + offset)) +#else +#define CHECK_READ_WATCHPOINT(addr, offset) +#define CHECK_WRITE_WATCHPOINT(addr, offset) +#endif + static inline uint32 rotl32(uint32 n, uint32 c) { @@ -1127,6 +1156,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #if WASM_ENABLE_DEBUG_INTERP != 0 uint8 *frame_ip_orig = NULL; + WASMDebugInstance *debug_instance = wasm_exec_env_get_instance(exec_env); + bh_list *watch_point_list_read = &debug_instance->watch_point_list_read; + bh_list *watch_point_list_write = &debug_instance->watch_point_list_write; #endif #if WASM_ENABLE_LABELS_AS_VALUES != 0 @@ -1792,6 +1824,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(4); PUSH_I32(LOAD_I32(maddr)); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1806,6 +1839,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(8); PUSH_I64(LOAD_I64(maddr)); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1819,6 +1853,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(1); PUSH_I32(sign_ext_8_32(*(int8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1832,6 +1867,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(1); PUSH_I32((uint32)(*(uint8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1845,6 +1881,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(2); PUSH_I32(sign_ext_16_32(LOAD_I16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1858,6 +1895,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(2); PUSH_I32((uint32)(LOAD_U16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1871,6 +1909,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(1); PUSH_I64(sign_ext_8_64(*(int8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1884,6 +1923,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(1); PUSH_I64((uint64)(*(uint8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1897,6 +1937,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(2); PUSH_I64(sign_ext_16_64(LOAD_I16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1910,6 +1951,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(2); PUSH_I64((uint64)(LOAD_U16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1924,6 +1966,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(4); PUSH_I64(sign_ext_32_64(LOAD_I32(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1937,6 +1980,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(4); PUSH_I64((uint64)(LOAD_U32(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1953,6 +1997,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_I32(); CHECK_MEMORY_OVERFLOW(4); STORE_U32(maddr, frame_sp[1]); + CHECK_WRITE_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1969,6 +2014,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_MEMORY_OVERFLOW(8); PUT_I64_TO_ADDR((uint32 *)maddr, GET_I64_FROM_ADDR(frame_sp + 1)); + CHECK_WRITE_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -1993,7 +2039,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_MEMORY_OVERFLOW(2); STORE_U16(maddr, (uint16)sval); } - + CHECK_WRITE_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } @@ -2023,6 +2069,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, CHECK_MEMORY_OVERFLOW(4); STORE_U32(maddr, (uint32)sval); } + CHECK_WRITE_WATCHPOINT(addr, offset); (void)flags; HANDLE_OP_END(); } diff --git a/core/iwasm/libraries/debug-engine/debug_engine.c b/core/iwasm/libraries/debug-engine/debug_engine.c index 61b29da84..1b3db1d49 100644 --- a/core/iwasm/libraries/debug-engine/debug_engine.c +++ b/core/iwasm/libraries/debug-engine/debug_engine.c @@ -392,6 +392,8 @@ wasm_debug_instance_create(WASMCluster *cluster, int32 port) } bh_list_init(&instance->break_point_list); + bh_list_init(&instance->watch_point_list_read); + bh_list_init(&instance->watch_point_list_write); instance->cluster = cluster; exec_env = bh_list_first_elem(&cluster->exec_env_list); @@ -452,6 +454,23 @@ wasm_debug_instance_destroy_breakpoints(WASMDebugInstance *instance) } } +static void +wasm_debug_instance_destroy_watchpoints(WASMDebugInstance *instance, + bh_list *watchpoints) +{ + WASMDebugWatchPoint *watchpoint, *next; + + watchpoint = bh_list_first_elem(watchpoints); + while (watchpoint) { + next = bh_list_elem_next(watchpoint); + + bh_list_remove(watchpoints, watchpoint); + wasm_runtime_free(watchpoint); + + watchpoint = next; + } +} + void wasm_debug_instance_destroy(WASMCluster *cluster) { @@ -472,6 +491,10 @@ wasm_debug_instance_destroy(WASMCluster *cluster) /* destroy all breakpoints */ wasm_debug_instance_destroy_breakpoints(instance); + wasm_debug_instance_destroy_watchpoints( + instance, &instance->watch_point_list_read); + wasm_debug_instance_destroy_watchpoints( + instance, &instance->watch_point_list_write); os_mutex_destroy(&instance->wait_lock); os_cond_destroy(&instance->wait_cond); @@ -995,6 +1018,65 @@ wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr, return true; } +static bool +add_watchpoint(bh_list *list, uint64 addr, uint64 length) +{ + WASMDebugWatchPoint *watchpoint; + if (!(watchpoint = wasm_runtime_malloc(sizeof(WASMDebugWatchPoint)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory for " + "watchpoint"); + return false; + } + memset(watchpoint, 0, sizeof(WASMDebugWatchPoint)); + watchpoint->addr = addr; + watchpoint->length = length; + bh_list_insert(list, watchpoint); + return true; +} + +static bool +remove_watchpoint(bh_list *list, uint64 addr, uint64 length) +{ + WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list); + while (watchpoint) { + WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint); + if (watchpoint->addr == addr && watchpoint->length == length) { + bh_list_remove(list, watchpoint); + wasm_runtime_free(watchpoint); + } + watchpoint = next; + } + return true; +} + +bool +wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return add_watchpoint(&instance->watch_point_list_write, addr, length); +} + +bool +wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return remove_watchpoint(&instance->watch_point_list_write, addr, length); +} + +bool +wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return add_watchpoint(&instance->watch_point_list_read, addr, length); +} + +bool +wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return remove_watchpoint(&instance->watch_point_list_read, addr, length); +} + bool wasm_debug_instance_on_failure(WASMDebugInstance *instance) { diff --git a/core/iwasm/libraries/debug-engine/debug_engine.h b/core/iwasm/libraries/debug-engine/debug_engine.h index f7580a7b7..e12f827bd 100644 --- a/core/iwasm/libraries/debug-engine/debug_engine.h +++ b/core/iwasm/libraries/debug-engine/debug_engine.h @@ -36,6 +36,12 @@ typedef struct WASMDebugBreakPoint { uint64 orignal_data; } WASMDebugBreakPoint; +typedef struct WASMDebugWatchPoint { + bh_list_link next; + uint64 addr; + uint64 length; +} WASMDebugWatchPoint; + typedef enum debug_state_t { /* Debugger state conversion sequence: * DBG_LAUNCHING ---> APP_STOPPED <---> APP_RUNNING @@ -56,6 +62,8 @@ struct WASMDebugInstance { struct WASMDebugInstance *next; WASMDebugControlThread *control_thread; bh_list break_point_list; + bh_list watch_point_list_read; + bh_list watch_point_list_write; WASMCluster *cluster; uint32 id; korp_tid current_tid; @@ -184,6 +192,22 @@ bool wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr, uint64 length); +bool +wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length); + bool wasm_debug_instance_on_failure(WASMDebugInstance *instance); diff --git a/core/iwasm/libraries/debug-engine/handler.c b/core/iwasm/libraries/debug-engine/handler.c index 701dd0c0c..cf1a87c59 100644 --- a/core/iwasm/libraries/debug-engine/handler.c +++ b/core/iwasm/libraries/debug-engine/handler.c @@ -358,6 +358,14 @@ handle_general_query(WASMGDBServer *server, char *payload) send_thread_stop_status(server, status, tid); } + + if (!strcmp(name, "WatchpointSupportInfo")) { + os_mutex_lock(&tmpbuf_lock); + // Any uint32 is OK for the watchpoint support + snprintf(tmpbuf, MAX_PACKET_SIZE, "num:32;"); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } } void @@ -643,46 +651,125 @@ handle_get_write_memory(WASMGDBServer *server, char *payload) os_mutex_unlock(&tmpbuf_lock); } +void +handle_breakpoint_software_add(WASMGDBServer *server, uint64 addr, + size_t length) +{ + bool ret = wasm_debug_instance_add_breakpoint( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_breakpoint_software_remove(WASMGDBServer *server, uint64 addr, + size_t length) +{ + bool ret = wasm_debug_instance_remove_breakpoint( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_write_add(WASMGDBServer *server, uint64 addr, size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_write_add( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_write_remove(WASMGDBServer *server, uint64 addr, + size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_write_remove( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_read_add(WASMGDBServer *server, uint64 addr, size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_read_add( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_read_remove(WASMGDBServer *server, uint64 addr, size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_read_remove( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + void handle_add_break(WASMGDBServer *server, char *payload) { + int arg_c; size_t type, length; uint64 addr; - if (sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length) == 3) { - if (type == eBreakpointSoftware) { - bool ret = wasm_debug_instance_add_breakpoint( - (WASMDebugInstance *)server->thread->debug_instance, addr, - length); - if (ret) - write_packet(server, "OK"); - else - write_packet(server, "E01"); - return; - } + if ((arg_c = sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length)) + != 3) { + LOG_ERROR("Unsupported number of add break arguments %d", arg_c); + write_packet(server, ""); + return; + } + + switch (type) { + case eBreakpointSoftware: + handle_breakpoint_software_add(server, addr, length); + break; + case eWatchpointWrite: + handle_watchpoint_write_add(server, addr, length); + break; + case eWatchpointRead: + handle_watchpoint_read_add(server, addr, length); + break; + case eWatchpointReadWrite: + handle_watchpoint_write_add(server, addr, length); + handle_watchpoint_read_add(server, addr, length); + break; + default: + LOG_ERROR("Unsupported breakpoint type %d", type); + write_packet(server, ""); + break; } - write_packet(server, ""); } void handle_remove_break(WASMGDBServer *server, char *payload) { + int arg_c; size_t type, length; uint64 addr; - if (sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length) == 3) { - if (type == eBreakpointSoftware) { - bool ret = wasm_debug_instance_remove_breakpoint( - (WASMDebugInstance *)server->thread->debug_instance, addr, - length); - if (ret) - write_packet(server, "OK"); - else - write_packet(server, "E01"); - return; - } + if ((arg_c = sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length)) + != 3) { + LOG_ERROR("Unsupported number of remove break arguments %d", arg_c); + write_packet(server, ""); + return; + } + + switch (type) { + case eBreakpointSoftware: + handle_breakpoint_software_remove(server, addr, length); + break; + case eWatchpointWrite: + handle_watchpoint_write_remove(server, addr, length); + break; + case eWatchpointRead: + handle_watchpoint_read_remove(server, addr, length); + break; + case eWatchpointReadWrite: + handle_watchpoint_write_remove(server, addr, length); + handle_watchpoint_read_remove(server, addr, length); + break; + default: + LOG_ERROR("Unsupported breakpoint type %d", type); + write_packet(server, ""); + break; } - write_packet(server, ""); } void From bfd57558a7c4002a458631d2bc9e1ed110183b7e Mon Sep 17 00:00:00 2001 From: Huang Qi Date: Thu, 8 Dec 2022 09:38:20 +0800 Subject: [PATCH 08/33] Implement i32.rem_s and i32.rem_u intrinsic (#1789) --- core/iwasm/aot/aot_intrinsic.c | 17 +++++++++ core/iwasm/aot/aot_intrinsic.h | 8 ++++ core/iwasm/aot/aot_reloc.h | 2 + core/iwasm/compilation/aot_emit_numberic.c | 43 +++++++++++++--------- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/core/iwasm/aot/aot_intrinsic.c b/core/iwasm/aot/aot_intrinsic.c index 7ac4b4743..5d27fc9e0 100644 --- a/core/iwasm/aot/aot_intrinsic.c +++ b/core/iwasm/aot/aot_intrinsic.c @@ -69,6 +69,8 @@ static const aot_intrinsic g_intrinsic_mapping[] = { { "f64.const", NULL, AOT_INTRINSIC_FLAG_F64_CONST }, { "i64.div_s", "aot_intrinsic_i64_div_s", AOT_INTRINSIC_FLAG_I64_DIV_S}, { "i32.div_u", "aot_intrinsic_i32_div_u", AOT_INTRINSIC_FLAG_I32_DIV_U}, + { "i32.rem_s", "aot_intrinsic_i32_rem_s", AOT_INTRINSIC_FLAG_I32_REM_S}, + { "i32.rem_u", "aot_intrinsic_i32_rem_u", AOT_INTRINSIC_FLAG_I32_REM_U}, { "i64.div_u", "aot_intrinsic_i64_div_u", AOT_INTRINSIC_FLAG_I64_DIV_U}, { "i64.rem_s", "aot_intrinsic_i64_rem_s", AOT_INTRINSIC_FLAG_I64_REM_S}, { "i64.rem_u", "aot_intrinsic_i64_rem_u", AOT_INTRINSIC_FLAG_I64_REM_U}, @@ -508,6 +510,18 @@ aot_intrinsic_i32_div_u(uint32 l, uint32 r) return l / r; } +int32 +aot_intrinsic_i32_rem_s(int32 l, int32 r) +{ + return l % r; +} + +uint32 +aot_intrinsic_i32_rem_u(uint32 l, uint32 r) +{ + return l % r; +} + uint64 aot_intrinsic_i64_div_u(uint64 l, uint64 r) { @@ -580,6 +594,8 @@ static void add_i32_common_intrinsics(AOTCompContext *comp_ctx) { add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_DIV_U); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_REM_S); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_REM_U); } static void @@ -675,6 +691,7 @@ aot_intrinsic_fill_capability_flags(AOTCompContext *comp_ctx) return; if (!strncmp(comp_ctx->target_arch, "thumb", 5)) { + add_i32_common_intrinsics(comp_ctx); if (!strcmp(comp_ctx->target_cpu, "cortex-m7")) { } else if (!strcmp(comp_ctx->target_cpu, "cortex-m4")) { diff --git a/core/iwasm/aot/aot_intrinsic.h b/core/iwasm/aot/aot_intrinsic.h index ae66037f2..553d67247 100644 --- a/core/iwasm/aot/aot_intrinsic.h +++ b/core/iwasm/aot/aot_intrinsic.h @@ -60,6 +60,8 @@ extern "C" { #define AOT_INTRINSIC_FLAG_F32_CONST AOT_INTRINSIC_FLAG(0, 26) #define AOT_INTRINSIC_FLAG_I32_CONST AOT_INTRINSIC_FLAG(0, 27) #define AOT_INTRINSIC_FLAG_I32_DIV_U AOT_INTRINSIC_FLAG(0, 28) +#define AOT_INTRINSIC_FLAG_I32_REM_S AOT_INTRINSIC_FLAG(0, 29) +#define AOT_INTRINSIC_FLAG_I32_REM_U AOT_INTRINSIC_FLAG(0, 30) #define AOT_INTRINSIC_FLAG_F64_FADD AOT_INTRINSIC_FLAG(1, 0) #define AOT_INTRINSIC_FLAG_F64_FSUB AOT_INTRINSIC_FLAG(1, 1) @@ -260,6 +262,12 @@ aot_intrinsic_i64_div_s(int64 l, int64 r); uint32 aot_intrinsic_i32_div_u(uint32 l, uint32 r); +int32 +aot_intrinsic_i32_rem_s(int32 l, int32 r); + +uint32 +aot_intrinsic_i32_rem_u(uint32 l, uint32 r); + uint64 aot_intrinsic_i64_div_u(uint64 l, uint64 r); diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h index 1ae52302c..41f83bb62 100644 --- a/core/iwasm/aot/aot_reloc.h +++ b/core/iwasm/aot/aot_reloc.h @@ -115,6 +115,8 @@ typedef struct { REG_SYM(aot_intrinsic_i64_bit_or), \ REG_SYM(aot_intrinsic_i64_bit_and), \ REG_SYM(aot_intrinsic_i32_div_u), \ + REG_SYM(aot_intrinsic_i32_rem_s), \ + REG_SYM(aot_intrinsic_i32_rem_u), \ #define REG_COMMON_SYMBOLS \ REG_SYM(aot_set_exception_with_id), \ diff --git a/core/iwasm/compilation/aot_emit_numberic.c b/core/iwasm/compilation/aot_emit_numberic.c index d9bfca517..396fc0e9e 100644 --- a/core/iwasm/compilation/aot_emit_numberic.c +++ b/core/iwasm/compilation/aot_emit_numberic.c @@ -31,7 +31,7 @@ #define LLVM_BUILD_OP_OR_INTRINSIC(Op, left, right, res, intrinsic, name, \ err_ret) \ do { \ - if (comp_ctx->disable_llvm_intrinsics && !is_i32 \ + if (comp_ctx->disable_llvm_intrinsics \ && aot_intrinsic_check_capability(comp_ctx, intrinsic)) { \ res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, \ param_types[0], param_types, 2, \ @@ -234,8 +234,6 @@ compile_op_float_min_max(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, nan = LLVMConstRealOfString(ret_type, "NaN"); char *intrinsic = is_min ? (is_f32 ? "llvm.minnum.f32" : "llvm.minnum.f64") : (is_f32 ? "llvm.maxnum.f32" : "llvm.maxnum.f64"); - bool is_i32 = is_f32; - CHECK_LLVM_CONST(nan); param_types[0] = param_types[1] = ret_type; @@ -292,11 +290,13 @@ compile_op_float_min_max(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } if (is_min) - LLVM_BUILD_OP_OR_INTRINSIC(Or, left_int, right_int, tmp, "i64.or", - "tmp_int", false); + LLVM_BUILD_OP_OR_INTRINSIC(Or, left_int, right_int, tmp, + is_f32 ? "i32.or" : "i64.or", "tmp_int", + false); else - LLVM_BUILD_OP_OR_INTRINSIC(And, left_int, right_int, tmp, "i64.and", - "tmp_int", false); + LLVM_BUILD_OP_OR_INTRINSIC(And, left_int, right_int, tmp, + is_f32 ? "i32.and" : "i64.and", "tmp_int", + false); if (!(tmp = LLVMBuildBitCast(comp_ctx->builder, tmp, ret_type, "tmp"))) { aot_set_last_error("llvm build bitcast fail."); @@ -402,7 +402,8 @@ compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMPositionBuilderAtEnd(comp_ctx->builder, no_overflow_block); LLVM_BUILD_OP_OR_INTRINSIC(SRem, left, right, no_overflow_value, - "i64.rem_s", "rem_s", false); + is_i32 ? "i32.rem_s" : "i64.rem_s", "rem_s", + false); /* Jump to rems_end block */ if (!LLVMBuildBr(comp_ctx->builder, rems_end_block)) { @@ -516,20 +517,24 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Build div */ switch (arith_op) { case INT_DIV_S: - LLVM_BUILD_OP_OR_INTRINSIC(SDiv, left, right, res, - "i64.div_s", "div_s", false); + LLVM_BUILD_OP_OR_INTRINSIC( + SDiv, left, right, res, + is_i32 ? "i32.div_s" : "i64.div_s", "div_s", false); break; case INT_DIV_U: - LLVM_BUILD_OP_OR_INTRINSIC(UDiv, left, right, res, - "i64.div_u", "div_u", false); + LLVM_BUILD_OP_OR_INTRINSIC( + UDiv, left, right, res, + is_i32 ? "i32.div_u" : "i64.div_u", "div_u", false); break; case INT_REM_S: - LLVM_BUILD_OP_OR_INTRINSIC(SRem, left, right, res, - "i64.rem_s", "rem_s", false); + LLVM_BUILD_OP_OR_INTRINSIC( + SRem, left, right, res, + is_i32 ? "i32.rem_s" : "i64.rem_s", "rem_s", false); break; case INT_REM_U: - LLVM_BUILD_OP_OR_INTRINSIC(URem, left, right, res, - "i64.rem_u", "rem_u", false); + LLVM_BUILD_OP_OR_INTRINSIC( + URem, left, right, res, + is_i32 ? "i32.rem_u" : "i64.rem_u", "rem_u", false); break; default: bh_assert(0); @@ -568,7 +573,8 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, check_overflow_succ))) goto fail; - LLVM_BUILD_OP_OR_INTRINSIC(SDiv, left, right, res, "i64.div_s", + LLVM_BUILD_OP_OR_INTRINSIC(SDiv, left, right, res, + is_i32 ? "i32.div_s" : "i64.div_s", "div_s", false); PUSH_INT(res); return true; @@ -594,7 +600,8 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return compile_rems(comp_ctx, func_ctx, left, right, overflow, is_i32); case INT_REM_U: - LLVM_BUILD_OP_OR_INTRINSIC(URem, left, right, res, "i64.rem_u", + LLVM_BUILD_OP_OR_INTRINSIC(URem, left, right, res, + is_i32 ? "i32.rem_u" : "i64.rem_u", "rem_u", false); PUSH_INT(res); return true; From 8fa7f1c4cf6333aee6be506ac291009360c84ca2 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Thu, 8 Dec 2022 12:21:34 +0800 Subject: [PATCH 09/33] Upgrade version number to 1.1.2 (#1788) Upgrade version number to 1.1.2, update RELEASE_NOTES.md and clear several compile warnings. --- RELEASE_NOTES.md | 109 +++++++++++++++++++ core/iwasm/interpreter/wasm_interp_classic.c | 4 +- core/shared/utils/bh_log.c | 4 +- core/version.h | 2 +- 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 86cdbf6a3..faba18c8e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -12,6 +12,115 @@ --- +## WAMR-1.1.2 + +### Breaking Changes +Remove the LLVM MCJIT mode, replace it with LLVM ORC JIT eager mode +Add option to pass user data to the allocator functions of RuntimeInitArgs +Change how iwasm returns: + return 1 if an exception was thrown, else + return the wasi exit code if the wasm app is a wasi app, else + keep the same behavior as before +Enable bulk memory by default + +### New Features +Add control for the native stack check with hardware trap +Add memory watchpoint support to debugger +Add wasm_module_obtain() to clone wasm_module_t +Implement Fast JIT dump call stack and perf profiling +esp-idf: Add socket support for esp-idf platform + +### Bug Fixes +Fix XIP issue caused by rem_s on RISC-V +Fix XIP issues of fp to int cast and int rem/div +Fix missing float cmp for XIP +Correct the arch name for armv7a on NuttX +Fix issue of restoring wasm operand stack +Fix issue of thumb relocation R_ARM_THM_MOVT_ABS +Fix fast jit issue of translating opcode i32.rem_s/i64.rem_s +Fix interp/fast-jit float min/max issues +Fix missing intrinsics for risc-v which were reported by spec test +wasm-c-api: Fix init/destroy thread env multiple times issue +Fix wasm-c-api import func link issue in wasm_instance_new +Fix sample ref-types/wasm-c-api build error with wat2wasm low version +Fix zephyr sample build errors +Fix source debugger error handling: continue executing when detached +Fix scenario where the timeout for atomic wait is set to negative number + +### Enhancements +Refactor the layout of interpreter and AOT module instance +Refactor LLVM JIT: remove mcjit and legacy pass manager, upgrade to ORCv2 JIT +Refine Fast JIT call indirect and call native process +Refine Fast JIT accessing memory/table instance and global data +Refine AOT exception check when function return +Enable source debugger reconnection +Add wasm_runtime_get_wasi_exit_code +linux-sgx: Use non-destructive modes for opening files using SGX IPFS +Add wasm_runtime_unregister_natives +Implement invokeNative asm code for MinGW +Add wamr Blog link and Gitbook link to readme +Remove unnecessary app heap memory clean operations to reduce process RSS +Normalize how the global heap pool is configured across iwasm apps +Refine the stack frame size check in interpreter +Enlarge the default wasm operand stack size to 64KB +Use cmake POSITION_INDEPENDENT_CODE instead of hardcoding -pie -fPIE +Implement R_ARM_THM_MOVT_[ABS|REPL] for thumb +Suppress the warnings when building with GCC11 +samples/native-lib: Add a bit more complicated example +Add mutex initializer for wasm-c-api engine operations +XIP adaptation for xtensa platform +Update libuv version number +Remove an improper assumption when creating wasm_trap +Avoid initialize LLVM repeatedly +linux-sgx: Improve the remote attestation +linux-sgx: Improve the documentation of SGX-RA sample +linux-sgx: Allow to open files with arbitrary paths in the sandbox using IPFS +Avoid raising exception when debugging with VSCode +wamr-test-suites: Update runtest.py to support python3 +Enable Nuttx spec test option and register aot symbols +Use wabt binary instead of building from source in spec test +nuttx: Enable ref types by Kconfig +Update xtensa LLVM version to 15.x +Add bh_print_proc_mem() to dump memory info of current process +Create trap for error message when wasm_instance_new fails +wamr-test-suites: Add support for ARM/RISCV by QEMU +Enable to compile WAMR on platforms that don't support IPV6 +Fix warnings in the posix socket implementation +Update document for MacOS compilation +Install patched LLDB on vscode extension activation +Add ARM aeabi memcpy/memmove/memset symbols for AOT bulk memory ops + +### Others +Add CIs to release new version and publish binary files +Add more compilation groups of fast jit into CI +Enable spec test on nuttx and daily run it + +--- + +## WAMR-1.1.1 + +- Implement Linux SGX socket API getpeername, recvfrom and sendto +- Implement Linux SGX POSIX calls based on getsockname and set/getbool +- Integrate WASI-NN into WAMR: support TensorFlow/CPU/F32 in the first stage +- Add timeout send/recv and multicast client/server socket examples +- Support cross building and linking LLVM shared libs for wamrc +- Add darwin support for app_framework +- Add ios support for product-mini +- Update export_native_api.md: Relax the "ground rule" +- wasm_export.h: Add comments on wasm_runtime_register_natives +- Remove unused wasm_runtime_is_module_registered +- samples/multi-module: Examine module registration a bit +- samples/native-lib: Fix exec_env type +- Fix Linux SGX directional OCALL parameter for getsockname +- Fix threads issue to enable running threads spec proposal test cases +- Fix the "register native with iwasm" stuff for macOS +- Fix issues in assemblyscript lib +- Wrap wasi_socket_ext api with extern "C" to fix link failure with cxx project +- Fix invalid size of memory allocated in wasi init +- posix_thread.c: Avoid sem_getvalue deprecation warning on macOS + +--- + ## WAMR-1.1.0 - Extend support for Socket API: diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index aa15b41a7..54fcf8d0b 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -95,8 +95,8 @@ typedef float64 CellType_F64; #define CHECK_WRITE_WATCHPOINT(addr, offset) \ CHECK_WATCHPOINT(watch_point_list_write, WASM_ADDR_OFFSET(addr + offset)) #else -#define CHECK_READ_WATCHPOINT(addr, offset) -#define CHECK_WRITE_WATCHPOINT(addr, offset) +#define CHECK_READ_WATCHPOINT(addr, offset) (void)0 +#define CHECK_WRITE_WATCHPOINT(addr, offset) (void)0 #endif static inline uint32 diff --git a/core/shared/utils/bh_log.c b/core/shared/utils/bh_log.c index 03dde4926..78c058065 100644 --- a/core/shared/utils/bh_log.c +++ b/core/shared/utils/bh_log.c @@ -103,5 +103,5 @@ bh_log_proc_mem(const char *function, uint32 line) { char prompt[128] = { 0 }; snprintf(prompt, sizeof(prompt), "[MEM] %s(...) L%" PRIu32, function, line); - return bh_print_proc_mem(prompt); -} \ No newline at end of file + bh_print_proc_mem(prompt); +} diff --git a/core/version.h b/core/version.h index 4a528c425..9834dae1c 100644 --- a/core/version.h +++ b/core/version.h @@ -7,5 +7,5 @@ #define _WAMR_VERSION_H_ #define WAMR_VERSION_MAJOR 1 #define WAMR_VERSION_MINOR 1 -#define WAMR_VERSION_PATCH 1 +#define WAMR_VERSION_PATCH 2 #endif From b08318b9c03e70f8a3c97f6cd4ba46bcf4ef221d Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Thu, 8 Dec 2022 15:39:55 +0800 Subject: [PATCH 10/33] Fix link cpp object file error (#1791) Error was reported when building docker image: /usr/bin/ld: libaotclib.a(aot_llvm_extra.cpp.o): relocation R_X86_64_32S against symbol `_ZN4llvm30TargetTransformInfoWrapperPass2IDE' can not be used when making a PIE object; recompile with -fPIC Add `-fPIC` to `CMAKE_CXX_FLAGS` and `CMAKE_SHARED_LIBRARY_LINK_C_FLAGS` to fix it. --- build-scripts/config_common.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 6b7c0cd78..008d7f47c 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -57,7 +57,9 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 8) if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") # Add -fPIC flag if build as 64-bit set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -fPIC") endif () else () include(CheckCCompilerFlag) From fbb369473d1f891b6321d64aaa4e739f79717590 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Thu, 8 Dec 2022 18:14:02 +0800 Subject: [PATCH 11/33] Fix link cxx object file error when building wamrc (#1794) --- wamr-compiler/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wamr-compiler/CMakeLists.txt b/wamr-compiler/CMakeLists.txt index 53db4a6d6..75c85a77a 100644 --- a/wamr-compiler/CMakeLists.txt +++ b/wamr-compiler/CMakeLists.txt @@ -109,7 +109,9 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 8) if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") # Add -fPIC flag if build as 64-bit set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -fPIC") endif () else () add_definitions (-m32) From 97a255ea83d818935af3a97d10d4c657541d8e20 Mon Sep 17 00:00:00 2001 From: "liang.he" Date: Thu, 8 Dec 2022 20:09:49 +0800 Subject: [PATCH 12/33] Move the release note template content to the end of file (#1795) Fix the issue that the release notes cannot be successfully created when releasing a new version. --- RELEASE_NOTES.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index faba18c8e..9bc76ed4a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,17 +1,3 @@ -## WAMR-X.Y.Z - -### Breaking Changes - -### New Features - -### Bug Fixes - -### Enhancements - -### Others - ---- - ## WAMR-1.1.2 ### Breaking Changes @@ -225,3 +211,17 @@ Enable spec test on nuttx and daily run it - Fix build error on alios platform --- + +## WAMR-X.Y.Z + +### Breaking Changes + +### New Features + +### Bug Fixes + +### Enhancements + +### Others + +--- From c613866bace03b09e527e7a36d611df284b2d18a Mon Sep 17 00:00:00 2001 From: Huang Qi Date: Tue, 13 Dec 2022 10:48:51 +0800 Subject: [PATCH 13/33] Implement i32.div_s (#1792) --- core/iwasm/aot/aot_intrinsic.c | 10 ++++++++++ core/iwasm/aot/aot_intrinsic.h | 4 ++++ core/iwasm/aot/aot_reloc.h | 1 + 3 files changed, 15 insertions(+) diff --git a/core/iwasm/aot/aot_intrinsic.c b/core/iwasm/aot/aot_intrinsic.c index 5d27fc9e0..ee1d4f92f 100644 --- a/core/iwasm/aot/aot_intrinsic.c +++ b/core/iwasm/aot/aot_intrinsic.c @@ -68,6 +68,7 @@ static const aot_intrinsic g_intrinsic_mapping[] = { { "f32.const", NULL, AOT_INTRINSIC_FLAG_F32_CONST }, { "f64.const", NULL, AOT_INTRINSIC_FLAG_F64_CONST }, { "i64.div_s", "aot_intrinsic_i64_div_s", AOT_INTRINSIC_FLAG_I64_DIV_S}, + { "i32.div_s", "aot_intrinsic_i32_div_s", AOT_INTRINSIC_FLAG_I32_DIV_S}, { "i32.div_u", "aot_intrinsic_i32_div_u", AOT_INTRINSIC_FLAG_I32_DIV_U}, { "i32.rem_s", "aot_intrinsic_i32_rem_s", AOT_INTRINSIC_FLAG_I32_REM_S}, { "i32.rem_u", "aot_intrinsic_i32_rem_u", AOT_INTRINSIC_FLAG_I32_REM_U}, @@ -504,6 +505,12 @@ aot_intrinsic_i64_div_s(int64 l, int64 r) return l / r; } +int32 +aot_intrinsic_i32_div_s(int32 l, int32 r) +{ + return l / r; +} + uint32 aot_intrinsic_i32_div_u(uint32 l, uint32 r) { @@ -593,6 +600,7 @@ add_i64_common_intrinsics(AOTCompContext *comp_ctx) static void add_i32_common_intrinsics(AOTCompContext *comp_ctx) { + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_DIV_S); add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_DIV_U); add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_REM_S); add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_REM_U); @@ -691,6 +699,7 @@ aot_intrinsic_fill_capability_flags(AOTCompContext *comp_ctx) return; if (!strncmp(comp_ctx->target_arch, "thumb", 5)) { + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_CONST); add_i32_common_intrinsics(comp_ctx); if (!strcmp(comp_ctx->target_cpu, "cortex-m7")) { } @@ -705,6 +714,7 @@ aot_intrinsic_fill_capability_flags(AOTCompContext *comp_ctx) } } else if (!strncmp(comp_ctx->target_arch, "riscv", 5)) { + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_CONST); /* * Note: Use builtin intrinsics since hardware float operation * will cause rodata relocation diff --git a/core/iwasm/aot/aot_intrinsic.h b/core/iwasm/aot/aot_intrinsic.h index 553d67247..2123058b9 100644 --- a/core/iwasm/aot/aot_intrinsic.h +++ b/core/iwasm/aot/aot_intrinsic.h @@ -62,6 +62,7 @@ extern "C" { #define AOT_INTRINSIC_FLAG_I32_DIV_U AOT_INTRINSIC_FLAG(0, 28) #define AOT_INTRINSIC_FLAG_I32_REM_S AOT_INTRINSIC_FLAG(0, 29) #define AOT_INTRINSIC_FLAG_I32_REM_U AOT_INTRINSIC_FLAG(0, 30) +#define AOT_INTRINSIC_FLAG_I32_DIV_S AOT_INTRINSIC_FLAG(0, 31) #define AOT_INTRINSIC_FLAG_F64_FADD AOT_INTRINSIC_FLAG(1, 0) #define AOT_INTRINSIC_FLAG_F64_FSUB AOT_INTRINSIC_FLAG(1, 1) @@ -259,6 +260,9 @@ aot_intrinsic_f64_cmp(AOTFloatCond cond, float64 lhs, float64 rhs); int64 aot_intrinsic_i64_div_s(int64 l, int64 r); +int32 +aot_intrinsic_i32_div_s(int32 l, int32 r); + uint32 aot_intrinsic_i32_div_u(uint32 l, uint32 r); diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h index 41f83bb62..9b374051f 100644 --- a/core/iwasm/aot/aot_reloc.h +++ b/core/iwasm/aot/aot_reloc.h @@ -114,6 +114,7 @@ typedef struct { REG_SYM(aot_intrinsic_i64_rem_u), \ REG_SYM(aot_intrinsic_i64_bit_or), \ REG_SYM(aot_intrinsic_i64_bit_and), \ + REG_SYM(aot_intrinsic_i32_div_s), \ REG_SYM(aot_intrinsic_i32_div_u), \ REG_SYM(aot_intrinsic_i32_rem_s), \ REG_SYM(aot_intrinsic_i32_rem_u), \ From dbec3f02026c51110c114e110e8056a30b14f626 Mon Sep 17 00:00:00 2001 From: Huang Qi Date: Tue, 13 Dec 2022 11:32:35 +0800 Subject: [PATCH 14/33] Enable XIP in CI daily test (#1793) --- .github/workflows/spec_test_on_nuttx.yml | 2 +- tests/wamr-test-suites/spec-test-script/runtest.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/spec_test_on_nuttx.yml b/.github/workflows/spec_test_on_nuttx.yml index 5f6be08a9..35c588d89 100644 --- a/.github/workflows/spec_test_on_nuttx.yml +++ b/.github/workflows/spec_test_on_nuttx.yml @@ -25,7 +25,7 @@ jobs: wamr_test_option: [ # "-t fast-interp", "-t aot", - # "-t aot -X" + "-t aot -X" ] steps: - name: Install Utilities diff --git a/tests/wamr-test-suites/spec-test-script/runtest.py b/tests/wamr-test-suites/spec-test-script/runtest.py index ad1816463..a61f03b1d 100755 --- a/tests/wamr-test-suites/spec-test-script/runtest.py +++ b/tests/wamr-test-suites/spec-test-script/runtest.py @@ -948,15 +948,15 @@ def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r, output = ' elif test_target == "armv7": cmd += ["--target=armv7", "--target-abi=gnueabihf"] elif test_target == "thumbv7": - cmd += ["--target=thumbv7", "--target-abi=gnueabihf", "--cpu=cortex-a9"] + cmd += ["--target=thumbv7", "--target-abi=gnueabihf", "--cpu=cortex-a9", "--cpu-features=-neon"] elif test_target == "riscv32_ilp32": - cmd += ["--target=riscv32", "--target-abi=ilp32"] + cmd += ["--target=riscv32", "--target-abi=ilp32", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"] elif test_target == "riscv32_ilp32d": - cmd += ["--target=riscv32", "--target-abi=ilp32d"] + cmd += ["--target=riscv32", "--target-abi=ilp32d", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"] elif test_target == "riscv64_lp64": - cmd += ["--target=riscv64", "--target-abi=lp64"] + cmd += ["--target=riscv64", "--target-abi=lp64", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c"] elif test_target == "riscv64_lp64d": - cmd += ["--target=riscv64", "--target-abi=lp64d"] + cmd += ["--target=riscv64", "--target-abi=lp64d", "--cpu=generic-rv32", "--cpu-features=+m,+a,+c"] else: pass From d267121c88481be41d539960e8fe896b4bf7efea Mon Sep 17 00:00:00 2001 From: dongsheng28849455 <68947925+dongsheng28849455@users.noreply.github.com> Date: Tue, 13 Dec 2022 12:45:26 +0800 Subject: [PATCH 15/33] Fix XIP issue of handling 64-bit const in 32-bit target (#1803) - Handle i64 const like f64 const - Ensure i64/f64 const is stored on 8-byte aligned address --- core/iwasm/compilation/aot_llvm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index b6e8b1164..889ab259c 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -2229,11 +2229,11 @@ aot_get_native_symbol_index(AOTCompContext *comp_ctx, const char *symbol) if (idx < 0) { if (comp_ctx->pointer_size == sizeof(uint32) - && !strncmp(symbol, "f64#", 4)) { + && (!strncmp(symbol, "f64#", 4) || !strncmp(symbol, "i64#", 4))) { idx = bh_list_length(&comp_ctx->native_symbols); /* Add 4 bytes padding on 32-bit target to make sure that the f64 const is stored on 8-byte aligned address */ - if ((idx & 1) && !strncmp(comp_ctx->target_arch, "i386", 4)) { + if (idx & 1) { if (!insert_native_symbol(comp_ctx, "__ignore", idx)) { return -1; } @@ -2246,7 +2246,7 @@ aot_get_native_symbol_index(AOTCompContext *comp_ctx, const char *symbol) } if (comp_ctx->pointer_size == sizeof(uint32) - && !strncmp(symbol, "f64#", 4)) { + && (!strncmp(symbol, "f64#", 4) || !strncmp(symbol, "i64#", 4))) { /* f64 const occupies 2 pointer slots on 32-bit target */ if (!insert_native_symbol(comp_ctx, "__ignore", idx + 1)) { return -1; From b826a84cd6b1752a35dc181cd42ddf18b12e231b Mon Sep 17 00:00:00 2001 From: "liang.he" Date: Tue, 13 Dec 2022 14:23:37 +0800 Subject: [PATCH 16/33] Use boringssl instead of openssl to implement wasm cache loading (#1804) --- CMakeLists.txt | 4 +-- build-scripts/involve_boringssl.cmake | 41 +++++++++++++++++++++++++++ build-scripts/runtime_lib.cmake | 12 +++----- core/iwasm/common/wasm_c_api.c | 2 +- samples/wasm-c-api/CMakeLists.txt | 6 ++-- samples/wasm-c-api/src/clone.c | 1 + 6 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 build-scripts/involve_boringssl.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index d6466ba5a..be8eb1dc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,7 +136,7 @@ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) # STATIC LIBRARY add_library(iwasm_static STATIC ${WAMR_RUNTIME_LIB_SOURCE}) if (WAMR_BUILD_WASM_CACHE EQUAL 1) - target_link_libraries(iwasm_static OpenSSL::SSL) + target_link_libraries(iwasm_static PUBLIC boringssl_crypto) endif () set_target_properties (iwasm_static PROPERTIES OUTPUT_NAME vmlib) @@ -147,7 +147,7 @@ add_library (iwasm_shared SHARED ${WAMR_RUNTIME_LIB_SOURCE}) set_target_properties (iwasm_shared PROPERTIES OUTPUT_NAME iwasm) target_link_libraries (iwasm_shared ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) if (WAMR_BUILD_WASM_CACHE EQUAL 1) - target_link_libraries(iwasm_shared OpenSSL::SSL) + target_link_libraries(iwasm_shared boringssl_crypto) endif () if (MINGW) diff --git a/build-scripts/involve_boringssl.cmake b/build-scripts/involve_boringssl.cmake new file mode 100644 index 000000000..a0e069ba8 --- /dev/null +++ b/build-scripts/involve_boringssl.cmake @@ -0,0 +1,41 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +message(STATUS "involving boringssl...") + +include(ExternalProject) + +ExternalProject_Add(boringssl + PREFIX external/boringssl + # follow envoy, which tracks BoringSSL, which tracks Chromium + # https://github.com/envoyproxy/envoy/blob/main/bazel/repository_locations.bzl#L112 + # chromium-105.0.5195.37 (linux/beta) + URL https://github.com/google/boringssl/archive/098695591f3a2665fccef83a3732ecfc99acdcdd.tar.gz + URL_HASH SHA256=e141448cf6f686b6e9695f6b6459293fd602c8d51efe118a83106752cf7e1280 + DOWNLOAD_EXTRACT_TIMESTAMP NEW + # SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../external/boringssl + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/src/boringssl-build/libssl.a + ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ + && ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/src/boringssl-build/libcrypto.a + ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ + && ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/src/boringssl/src/include/openssl + ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/openssl +) + +add_library(boringssl_ssl STATIC IMPORTED GLOBAL) +set_target_properties( + boringssl_ssl + PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/libssl.a + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ +) +add_dependencies(boringssl_ssl boringssl) + +add_library(boringssl_crypto STATIC IMPORTED GLOBAL) +set_target_properties( + boringssl_crypto + PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/libcrypto.a + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ +) +add_dependencies(boringssl_crypto boringssl) \ No newline at end of file diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index 973318866..4130894dc 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -27,14 +27,6 @@ if (DEFINED EXTRA_SDK_INCLUDE_PATH) ) endif () -# Need exactly OpenSSL 1.1.1 -if (WAMR_BUILD_WASM_CACHE EQUAL 1) - # Set OPENSSL_ROOT_DIR to the root directory of an OpenSSL installation. - # Like: cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl - # Especially on MacOS - find_package(OpenSSL 1.1.1 EXACT REQUIRED) -endif () - # Set default options # Set WAMR_BUILD_TARGET, currently values supported: @@ -155,6 +147,10 @@ if (WAMR_BUILD_LIB_RATS EQUAL 1) include (${IWASM_DIR}/libraries/lib-rats/lib_rats.cmake) endif () +if (WAMR_BUILD_WASM_CACHE EQUAL 1) + include (${WAMR_ROOT_DIR}/build-scripts/involve_boringssl.cmake) +endif () + ####################### Common sources ####################### if (NOT MSVC) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -ffunction-sections -fdata-sections \ diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index 619e02297..ae4ceaf91 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -2183,7 +2183,7 @@ wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) #if WASM_ENABLE_WASM_CACHE != 0 /* if cached */ - SHA256((void *)binary->data, binary->num_elems, binary_hash); + SHA256((void *)binary->data, binary->num_elems, (uint8_t *)binary_hash); module_ex = try_reuse_loaded_module(store, binary_hash); if (module_ex) return module_ext_to_module(module_ex); diff --git a/samples/wasm-c-api/CMakeLists.txt b/samples/wasm-c-api/CMakeLists.txt index 02b97d4cf..876874055 100644 --- a/samples/wasm-c-api/CMakeLists.txt +++ b/samples/wasm-c-api/CMakeLists.txt @@ -94,7 +94,7 @@ endif() target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) if (WAMR_BUILD_WASM_CACHE EQUAL 1) - target_link_libraries(vmlib OpenSSL::SSL) + target_link_libraries(vmlib boringssl_crypto) endif () ################################################ @@ -165,7 +165,7 @@ foreach(EX ${EXAMPLES}) add_executable(${EX} ${SRC} ${UNCOMMON_SHARED_SOURCE} ${MM_UTIL}) set_target_properties (${EX} PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(${EX} PRIVATE ${UNCOMMON_SHARED_DIR}) - target_link_libraries(${EX} vmlib -lpthread -lm) + target_link_libraries(${EX} vmlib) if (MSVC) target_compile_definitions(${EX} PRIVATE WASM_API_EXTERN=) endif() @@ -206,7 +206,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") add_custom_command( OUTPUT ${EX}_leak_check.report DEPENDS ${EX} ${EX}_WASM - COMMAND ${VALGRIND} --tool=memcheck --leak-check=summary -- ./${EX} > ${EX}_leak_check.report 2>&1 + COMMAND ${VALGRIND} --tool=memcheck --leak-check=full -- ./${EX} > ${EX}_leak_check.report 2>&1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) add_custom_target(${EX}_LEAK_TEST ALL diff --git a/samples/wasm-c-api/src/clone.c b/samples/wasm-c-api/src/clone.c index ce8ebd8bd..da83b3790 100644 --- a/samples/wasm-c-api/src/clone.c +++ b/samples/wasm-c-api/src/clone.c @@ -522,6 +522,7 @@ main() pthread_mutex_unlock(&ready_go_lock); pthread_cond_broadcast(&ready_go_cond); + sleep(3); for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) { if (tids[i] != 0) pthread_join(tids[i], NULL); From 4d0f5cfade9549ef3c9459e4f3e5b6226f3c1ffa Mon Sep 17 00:00:00 2001 From: Callum Macmillan Date: Tue, 13 Dec 2022 07:16:43 +0000 Subject: [PATCH 17/33] Fix watchpoint segfault when using debug interp without server (#1806) --- core/iwasm/interpreter/wasm_interp_classic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 54fcf8d0b..5fe814384 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1157,8 +1157,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #if WASM_ENABLE_DEBUG_INTERP != 0 uint8 *frame_ip_orig = NULL; WASMDebugInstance *debug_instance = wasm_exec_env_get_instance(exec_env); - bh_list *watch_point_list_read = &debug_instance->watch_point_list_read; - bh_list *watch_point_list_write = &debug_instance->watch_point_list_write; + bh_list *watch_point_list_read = + debug_instance ? &debug_instance->watch_point_list_read : NULL; + bh_list *watch_point_list_write = + debug_instance ? &debug_instance->watch_point_list_write : NULL; #endif #if WASM_ENABLE_LABELS_AS_VALUES != 0 From 97fda83c94d5348d338399dda6b80df791e2ac09 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Wed, 14 Dec 2022 09:42:03 +0800 Subject: [PATCH 18/33] Publish the docker image with tar/zip files (#1808) And fix issue found in fast jit call indirect. --- .github/workflows/build_docker_images.yml | 87 ++++++++++++++++------ .github/workflows/release_process.yml | 6 +- core/iwasm/fast-jit/fe/jit_emit_function.c | 6 +- test-tools/wamr-ide/README.md | 36 ++++++--- 4 files changed, 93 insertions(+), 42 deletions(-) diff --git a/.github/workflows/build_docker_images.yml b/.github/workflows/build_docker_images.yml index 7b101d2d2..b9892279f 100644 --- a/.github/workflows/build_docker_images.yml +++ b/.github/workflows/build_docker_images.yml @@ -6,6 +6,10 @@ name: Create and publish Docker images on: workflow_call: inputs: + upload_url: + description: upload binary assets to the URL of release + type: string + required: true ver_num: description: a semantic version number. type: string @@ -14,35 +18,72 @@ on: jobs: build-and-push-images: runs-on: ubuntu-22.04 - permissions: - contents: read - packages: write steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Downcase github actor - id: downcase_github_actor - uses: ASzc/change-string-case-action@v2 - with: - string: ${{ github.actor }} - - - name: Login to the Container registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ steps.downcase_github_actor.outputs.lowercase }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push Docker image(wasm-toolchain:${{ inputs.ver_num }}) to Container registry + - name: Build and save Docker image(wasm-debug-server:${{ inputs.ver_num }}) to tar file run: | - docker build -t ghcr.io/${{ steps.downcase_github_actor.outputs.lowercase }}/wasm-toolchain:${{ inputs.ver_num }} . - docker push ghcr.io/${{ steps.downcase_github_actor.outputs.lowercase }}/wasm-toolchain:${{ inputs.ver_num }} + docker build -t wasm-debug-server:${{ inputs.ver_num }} . + docker save -o wasm-debug-server.tar wasm-debug-server:${{ inputs.ver_num }} + working-directory: test-tools/wamr-ide/WASM-Debug-Server/Docker + + - name: compress the tar file + run: | + tar czf wasm-debug-server-${{ inputs.ver_num }}.tar.gz wasm-debug-server.tar + zip wasm-debug-server-${{ inputs.ver_num }}.zip wasm-debug-server.tar + working-directory: test-tools/wamr-ide/WASM-Debug-Server/Docker + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Debug-Server/Docker/wasm-debug-server-${{ inputs.ver_num }}.tar.gz + asset_name: wasm-debug-server-${{ inputs.ver_num }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Debug-Server/Docker/wasm-debug-server-${{ inputs.ver_num }}.zip + asset_name: wasm-debug-server-${{ inputs.ver_num }}.zip + asset_content_type: application/zip + + - name: Build and save Docker image(wasm-toolchain:${{ inputs.ver_num }}) to tar file + run: | + docker build -t wasm-toolchain:${{ inputs.ver_num }} . + docker save -o wasm-toolchain.tar wasm-toolchain:${{ inputs.ver_num }} working-directory: test-tools/wamr-ide/WASM-Toolchain/Docker - - name: Build and push Docker image(wasm-debug-server:${{ inputs.ver_num }}) to Container registry + - name: compress the tar file run: | - docker build -t ghcr.io/${{ steps.downcase_github_actor.outputs.lowercase }}/wasm-debug-server:${{ inputs.ver_num }} . - docker push ghcr.io/${{ steps.downcase_github_actor.outputs.lowercase }}/wasm-debug-server:${{ inputs.ver_num }} - working-directory: test-tools/wamr-ide/WASM-Debug-Server/Docker + tar czf wasm-toolchain-${{ inputs.ver_num }}.tar.gz wasm-toolchain.tar + zip wasm-toolchain-${{ inputs.ver_num }}.zip wasm-toolchain.tar + working-directory: test-tools/wamr-ide/WASM-Toolchain/Docker + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Toolchain/Docker/wasm-toolchain-${{ inputs.ver_num }}.tar.gz + asset_name: wasm-toolchain-${{ inputs.ver_num }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Toolchain/Docker/wasm-toolchain-${{ inputs.ver_num }}.zip + asset_name: wasm-toolchain-${{ inputs.ver_num }}.zip + asset_content_type: application/zip + diff --git a/.github/workflows/release_process.yml b/.github/workflows/release_process.yml index e48c8ffeb..aae2f890a 100644 --- a/.github/workflows/release_process.yml +++ b/.github/workflows/release_process.yml @@ -37,9 +37,6 @@ jobs: echo "${extract_result}" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV - - name: check output - run: echo 'the release note is "${{ env.RELEASE_NOTE }}"' - - name: create a release id: create_release uses: actions/create-release@v1 @@ -163,8 +160,9 @@ jobs: needs: [create_tag, create_release] uses: ./.github/workflows/build_docker_images.yml with: + upload_url: ${{ needs.create_release.outputs.upload_url }} ver_num: ${{ needs.create_tag.outputs.new_ver }} - + # # WAMR_LLDB release_wamr_lldb_on_ubuntu_2004: diff --git a/core/iwasm/fast-jit/fe/jit_emit_function.c b/core/iwasm/fast-jit/fe/jit_emit_function.c index 5b0728749..ce22a950a 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_function.c +++ b/core/iwasm/fast-jit/fe/jit_emit_function.c @@ -624,19 +624,19 @@ jit_compile_op_call_indirect(JitCompContext *cc, uint32 type_idx, NEW_CONST(I32, offset_of_local(n))); break; case VALUE_TYPE_I64: - res = jit_cc_new_reg_I32(cc); + res = jit_cc_new_reg_I64(cc); GEN_INSN(LDI64, res, argv, NEW_CONST(I32, 0)); GEN_INSN(STI64, res, cc->fp_reg, NEW_CONST(I32, offset_of_local(n))); break; case VALUE_TYPE_F32: - res = jit_cc_new_reg_I32(cc); + res = jit_cc_new_reg_F32(cc); GEN_INSN(LDF32, res, argv, NEW_CONST(I32, 0)); GEN_INSN(STF32, res, cc->fp_reg, NEW_CONST(I32, offset_of_local(n))); break; case VALUE_TYPE_F64: - res = jit_cc_new_reg_I32(cc); + res = jit_cc_new_reg_F64(cc); GEN_INSN(LDF64, res, argv, NEW_CONST(I32, 0)); GEN_INSN(STF64, res, cc->fp_reg, NEW_CONST(I32, offset_of_local(n))); diff --git a/test-tools/wamr-ide/README.md b/test-tools/wamr-ide/README.md index 9b78ea7ed..7e2c05ce7 100644 --- a/test-tools/wamr-ide/README.md +++ b/test-tools/wamr-ide/README.md @@ -37,23 +37,35 @@ under `resource/debug/bin`. - Ubuntu Bionic 18.04(LTS) ``` -#### 3. Pull docker images from the registry(recommended) or build docker images on the host +#### 3. Load docker images from the release tar file or build docker images on the host -##### 3.1 Pull docker images from registry +##### 3.1 Load docker images from the release tar file -From now on, for each release, we have the same version tagged docker image pushed to GitHub package. +From now on, for each release, we have the same version tagged docker image saved as a tar file, which you can find and download in the release. -You could simply pull a certain version of docker images using the following commands: +You could download the tar archive files for docker images from the release, and then load them using the following commands: ```sh -# pull and retag wasm-toolchain -docker pull ghcr.io/bytecodealliance/wasm-toolchain:{version number} -docker tag ghcr.io/bytecodealliance/wasm-toolchain:{version number} wasm-toolchain:{version number} -docker rmi ghcr.io/bytecodealliance/wasm-toolchain:{version number} -# pull and retag wasm-debug-server -docker pull ghcr.io/bytecodealliance/wasm-debug-server:{version number} -docker tag ghcr.io/bytecodealliance/wasm-debug-server:{version number} wasm-debug-server:{version number} -docker rmi ghcr.io/bytecodealliance/wasm-debug-server:{version number} +# download the zip or tar.gz from release depending on your platform +# decompress and get the tar file + +# on Linux/MacOS, you could use tar +tar xf wasm-toolchain-{version number}.tar.gz +tar xf wasm-debug-server-{version number}.tar.gz +# or you could use unzip +unzip wasm-toolchain-{version number}.zip +unzip wasm-debug-server-{version number}.zip +# load wasm-toolchain +docker load --input wasm-toolchain.tar +# load wasm-debug-server +docker load --input wasm-debug-server.tar + +# on Windows, you could use any unzip software you like +# then loading docker images using powershell or git bash +# load wasm-toolchain +docker load --input ./wasm-toolchain.tar +# load wasm-debug-server +docker load --input ./wasm-debug-server.tar ``` ##### 3.2 Build docker images on host From fe210f02f2e3eee356794316fbb85292c0dbbfe4 Mon Sep 17 00:00:00 2001 From: TianlongLiang <111852609+TianlongLiang@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:15:32 +0800 Subject: [PATCH 19/33] vscode-extension: Run docker image with the same version as WAMR (#1815) --- .../resource/scripts/boot_debugger_server.bat | 2 +- .../resource/scripts/boot_debugger_server.sh | 2 +- .../resource/scripts/build.bat | 2 +- .../resource/scripts/build.sh | 2 +- .../VSCode-Extension/resource/scripts/run.bat | 2 +- .../VSCode-Extension/resource/scripts/run.sh | 2 +- .../VSCode-Extension/src/extension.ts | 13 ++- .../VSCode-Extension/src/taskProvider.ts | 15 ++-- .../src/utilities/directoryUtilities.ts | 86 ++++++++++++------- .../src/utilities/lldbUtilities.ts | 74 +++++++++++----- 10 files changed, 132 insertions(+), 68 deletions(-) diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat index 343049716..7fd1f024a 100644 --- a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.bat @@ -6,5 +6,5 @@ docker run --rm -it --name=wasm-debug-server-ctr ^ -v "%cd%":/mnt ^ -p 1234:1234 ^ - wasm-debug-server:1.0 ^ + wasm-debug-server:%2 ^ /bin/bash -c "./debug.sh %1" diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh index 97e290fce..169fb7e5f 100755 --- a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/boot_debugger_server.sh @@ -8,5 +8,5 @@ set -e docker run --rm -it --name=wasm-debug-server-ctr \ -v "$(pwd)":/mnt \ -p 1234:1234 \ - wasm-debug-server:1.0 \ + wasm-debug-server:$2 \ /bin/bash -c "./debug.sh $1" diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat index 1fe6ca41c..de415107a 100644 --- a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.bat @@ -7,5 +7,5 @@ docker run --rm --name=wasm-toolchain-ctr ^ -it -v "%cd%":/mnt ^ --env=PROJ_PATH="%cd%" ^ - wasm-toolchain:1.0 ^ + wasm-toolchain:%2 ^ /bin/bash -c "./build_wasm.sh %1" diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh index 04892b4ee..a8a42cc89 100755 --- a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/build.sh @@ -8,5 +8,5 @@ set -e docker run --rm --name=wasm-toolchain-ctr \ -it -v "$(pwd)":/mnt \ --env=PROJ_PATH="$(pwd)" \ - wasm-toolchain:1.0 \ + wasm-toolchain:$2 \ /bin/bash -c "./build_wasm.sh $1" diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat index 45d86bdab..af47f35ba 100644 --- a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.bat @@ -5,5 +5,5 @@ docker run --rm -it --name=wasm-debug-server-ctr ^ -v "%cd%":/mnt ^ - wasm-debug-server:1.0 ^ + wasm-debug-server:%2 ^ /bin/bash -c "./run.sh %1" diff --git a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh index 31961847f..670e57c1e 100755 --- a/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh +++ b/test-tools/wamr-ide/VSCode-Extension/resource/scripts/run.sh @@ -7,5 +7,5 @@ set -e docker run --rm -it --name=wasm-debug-server-ctr \ -v "$(pwd)":/mnt \ - wasm-debug-server:1.0 \ + wasm-debug-server:$2 \ /bin/bash -c "./run.sh $1" diff --git a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts index 7eb8b34a6..e1d33b4ee 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/extension.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/extension.ts @@ -18,7 +18,11 @@ import { } from './utilities/directoryUtilities'; import { decorationProvider } from './decorationProvider'; import { WasmDebugConfigurationProvider } from './debugConfigurationProvider'; -import { isLLDBInstalled, promptInstallLLDB } from './utilities/lldbUtilities'; +import { + isLLDBInstalled, + promptInstallLLDB, + getWAMRExtensionVersion, +} from './utilities/lldbUtilities'; let wasmTaskProvider: WasmTaskProvider; let wasmDebugConfigProvider: WasmDebugConfigurationProvider; @@ -43,6 +47,8 @@ export async function activate(context: vscode.ExtensionContext) { excludeFileArr = new Array(), scriptMap = new Map(); + const wamrVersion = getWAMRExtensionVersion(context); + /** * Get OS platform information for differ windows and linux execution script */ @@ -83,7 +89,7 @@ export async function activate(context: vscode.ExtensionContext) { typeMap.set('Debug', 'Debug'); typeMap.set('Destroy', 'Destroy'); - wasmTaskProvider = new WasmTaskProvider(typeMap, scriptMap); + wasmTaskProvider = new WasmTaskProvider(typeMap, scriptMap, wamrVersion); vscode.tasks.registerTaskProvider('wasm', wasmTaskProvider); @@ -670,7 +676,8 @@ export async function activate(context: vscode.ExtensionContext) { let _path = curWorkspace.concat( OS_PLATFORM === 'win32' ? '\\' - : OS_PLATFORM === 'linux' || OS_PLATFORM === 'darwin' + : OS_PLATFORM === 'linux' || + OS_PLATFORM === 'darwin' ? '/' : '', option diff --git a/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts b/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts index e5e1280ee..7b2408bd5 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/taskProvider.ts @@ -15,7 +15,8 @@ export interface OwnShellOption { export class WasmTaskProvider implements vscode.TaskProvider { constructor( public _type: Map, - public _script: Map + public _script: Map, + public _wamrVersion: string ) {} buildShellOption: OwnShellOption | undefined; @@ -31,7 +32,11 @@ export class WasmTaskProvider implements vscode.TaskProvider { let targetName = TargetConfigPanel.BUILD_ARGS.output_file_name.split('.')[0]; - if (os.platform() === 'linux' || os.platform() === 'darwin' || os.platform() === 'win32') { + if ( + os.platform() === 'linux' || + os.platform() === 'darwin' || + os.platform() === 'win32' + ) { /* build */ this.buildShellOption = { cmd: @@ -40,7 +45,7 @@ export class WasmTaskProvider implements vscode.TaskProvider { : (this._script.get('buildScript') as string), options: { executable: this._script.get('buildScript'), - shellArgs: [targetName, os.platform()], + shellArgs: [targetName, this._wamrVersion], }, }; @@ -52,7 +57,7 @@ export class WasmTaskProvider implements vscode.TaskProvider { : (this._script.get('debugScript') as string), options: { executable: this._script.get('debugScript'), - shellArgs: [targetName], + shellArgs: [targetName, this._wamrVersion], }, }; @@ -64,7 +69,7 @@ export class WasmTaskProvider implements vscode.TaskProvider { : (this._script.get('runScript') as string), options: { executable: this._script.get('runScript'), - shellArgs: [targetName], + shellArgs: [targetName, this._wamrVersion], }, }; diff --git a/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts b/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts index 348e5730a..c55be4717 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/utilities/directoryUtilities.ts @@ -137,53 +137,73 @@ export function checkFolderName(folderName: string) { return valid; } -export function downloadFile(url: string, destinationPath: string): Promise { +export function downloadFile( + url: string, + destinationPath: string +): Promise { return new Promise((resolve, reject) => { const file = fileSystem.createWriteStream(destinationPath); const stream = request(url, undefined, (error, response, body) => { if (response.statusCode !== 200) { - reject(new Error(`Download from ${url} failed with ${response.statusMessage}`)); + reject( + new Error( + `Download from ${url} failed with ${response.statusMessage}` + ) + ); } }).pipe(file); - stream.on("close", resolve); - stream.on("error", reject); + stream.on('close', resolve); + stream.on('error', reject); }); } -export function unzipFile(sourcePath: string, getDestinationFileName: (entryName: string) => string): Promise { +export function unzipFile( + sourcePath: string, + getDestinationFileName: (entryName: string) => string +): Promise { return new Promise((resolve, reject) => { const unzippedFilePaths: string[] = []; - yauzl.open(sourcePath, { lazyEntries: true }, function(error, zipfile) { - if (error) { - reject(error); - return; - } - zipfile.readEntry(); - zipfile.on("entry", function(entry) { - // This entry is a directory so skip it - if (/\/$/.test(entry.fileName)) { - zipfile.readEntry(); + yauzl.open( + sourcePath, + { lazyEntries: true }, + function (error, zipfile) { + if (error) { + reject(error); return; - } - - zipfile.openReadStream(entry, function(error, readStream) { - if (error) { - reject(error); + } + zipfile.readEntry(); + zipfile.on('entry', function (entry) { + // This entry is a directory so skip it + if (/\/$/.test(entry.fileName)) { + zipfile.readEntry(); return; } - readStream.on("end", () => zipfile.readEntry()); - const destinationFileName = getDestinationFileName(entry.fileName); - fileSystem.mkdirSync(path.dirname(destinationFileName), { recursive: true }); - const file = fileSystem.createWriteStream(destinationFileName); - readStream.pipe(file).on("error", reject); - unzippedFilePaths.push(destinationFileName); + zipfile.openReadStream(entry, function (error, readStream) { + if (error) { + reject(error); + return; + } + readStream.on('end', () => zipfile.readEntry()); + const destinationFileName = getDestinationFileName( + entry.fileName + ); + fileSystem.mkdirSync( + path.dirname(destinationFileName), + { recursive: true } + ); + + const file = + fileSystem.createWriteStream(destinationFileName); + readStream.pipe(file).on('error', reject); + unzippedFilePaths.push(destinationFileName); + }); }); - }); - zipfile.on("end", function() { - zipfile.close(); - resolve(unzippedFilePaths); - }); - }); + zipfile.on('end', function () { + zipfile.close(); + resolve(unzippedFilePaths); + }); + } + ); }); -} \ No newline at end of file +} diff --git a/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts b/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts index 50d468b94..e2eaec239 100644 --- a/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts +++ b/test-tools/wamr-ide/VSCode-Extension/src/utilities/lldbUtilities.ts @@ -7,27 +7,41 @@ import * as vscode from 'vscode'; import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; -import { checkIfFileExists, downloadFile, unzipFile } from './directoryUtilities'; +import { + checkIfFileExists, + downloadFile, + unzipFile, +} from './directoryUtilities'; -const LLDB_RESOURCE_DIR = "resource/debug"; -const LLDB_OS_DOWNLOAD_URL_SUFFIX_MAP: Partial> = { - "linux": "x86_64-ubuntu-22.04", - "darwin": "universal-macos-latest" +const LLDB_RESOURCE_DIR = 'resource/debug'; +const LLDB_OS_DOWNLOAD_URL_SUFFIX_MAP: Partial< + Record +> = { + linux: 'x86_64-ubuntu-22.04', + darwin: 'universal-macos-latest', }; -const WAMR_LLDB_NOT_SUPPORTED_ERROR = new Error("WAMR LLDB is not supported on this platform"); +const WAMR_LLDB_NOT_SUPPORTED_ERROR = new Error( + 'WAMR LLDB is not supported on this platform' +); function getLLDBUnzipFilePath(destinationFolder: string, filename: string) { - const dirs = filename.split("/"); - if (dirs[0] === "inst") { + const dirs = filename.split('/'); + if (dirs[0] === 'inst') { dirs.shift(); } return path.join(destinationFolder, ...dirs); } +export function getWAMRExtensionVersion( + context: vscode.ExtensionContext +): string { + return require(path.join(context.extensionPath, 'package.json')).version; +} + function getLLDBDownloadUrl(context: vscode.ExtensionContext): string { - const wamrVersion = require(path.join(context.extensionPath, "package.json")).version; + const wamrVersion = getWAMRExtensionVersion(context); const lldbOsUrlSuffix = LLDB_OS_DOWNLOAD_URL_SUFFIX_MAP[os.platform()]; if (!lldbOsUrlSuffix) { @@ -40,15 +54,25 @@ function getLLDBDownloadUrl(context: vscode.ExtensionContext): string { export function isLLDBInstalled(context: vscode.ExtensionContext): boolean { const extensionPath = context.extensionPath; const lldbOSDir = os.platform(); - const lldbBinaryPath = path.join(extensionPath, LLDB_RESOURCE_DIR, lldbOSDir, "bin", "lldb"); + const lldbBinaryPath = path.join( + extensionPath, + LLDB_RESOURCE_DIR, + lldbOSDir, + 'bin', + 'lldb' + ); return checkIfFileExists(lldbBinaryPath); } export async function promptInstallLLDB(context: vscode.ExtensionContext) { const extensionPath = context.extensionPath; - const setupPrompt = "setup"; - const skipPrompt = "skip"; - const response = await vscode.window.showWarningMessage('No LLDB instance found. Setup now?', setupPrompt, skipPrompt); + const setupPrompt = 'setup'; + const skipPrompt = 'skip'; + const response = await vscode.window.showWarningMessage( + 'No LLDB instance found. Setup now?', + setupPrompt, + skipPrompt + ); if (response === skipPrompt) { return; @@ -61,23 +85,31 @@ export async function promptInstallLLDB(context: vscode.ExtensionContext) { throw WAMR_LLDB_NOT_SUPPORTED_ERROR; } - const lldbDestinationFolder = path.join(extensionPath, LLDB_RESOURCE_DIR, destinationDir); - const lldbZipPath = path.join(lldbDestinationFolder, "bundle.zip"); + const lldbDestinationFolder = path.join( + extensionPath, + LLDB_RESOURCE_DIR, + destinationDir + ); + const lldbZipPath = path.join(lldbDestinationFolder, 'bundle.zip'); vscode.window.showInformationMessage(`Downloading LLDB...`); await downloadFile(downloadUrl, lldbZipPath); - vscode.window.showInformationMessage(`LLDB downloaded to ${lldbZipPath}. Installing...`); + vscode.window.showInformationMessage( + `LLDB downloaded to ${lldbZipPath}. Installing...` + ); - const lldbFiles = await unzipFile(lldbZipPath, filename => getLLDBUnzipFilePath(lldbDestinationFolder, filename)); + const lldbFiles = await unzipFile(lldbZipPath, filename => + getLLDBUnzipFilePath(lldbDestinationFolder, filename) + ); // Allow execution of lldb - lldbFiles.forEach(file => fs.chmodSync(file, "0775")); + lldbFiles.forEach(file => fs.chmodSync(file, '0775')); - vscode.window.showInformationMessage(`LLDB installed at ${lldbDestinationFolder}`); + vscode.window.showInformationMessage( + `LLDB installed at ${lldbDestinationFolder}` + ); // Remove the bundle.zip fs.unlink(lldbZipPath, () => {}); } - - From 7db49db7779d894ca4f5a09643c7905eaed62604 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Fri, 16 Dec 2022 14:16:58 +0800 Subject: [PATCH 20/33] Update release notes and fix issues reported by Coverity (#1813) --- .github/workflows/build_docker_images.yml | 4 ++-- RELEASE_NOTES.md | 3 +++ core/iwasm/libraries/debug-engine/handler.c | 4 ++-- test-tools/wamr-ide/README.md | 8 ++++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_docker_images.yml b/.github/workflows/build_docker_images.yml index b9892279f..819bf94c3 100644 --- a/.github/workflows/build_docker_images.yml +++ b/.github/workflows/build_docker_images.yml @@ -28,7 +28,7 @@ jobs: docker build -t wasm-debug-server:${{ inputs.ver_num }} . docker save -o wasm-debug-server.tar wasm-debug-server:${{ inputs.ver_num }} working-directory: test-tools/wamr-ide/WASM-Debug-Server/Docker - + - name: compress the tar file run: | tar czf wasm-debug-server-${{ inputs.ver_num }}.tar.gz wasm-debug-server.tar @@ -86,4 +86,4 @@ jobs: asset_path: test-tools/wamr-ide/WASM-Toolchain/Docker/wasm-toolchain-${{ inputs.ver_num }}.zip asset_name: wasm-toolchain-${{ inputs.ver_num }}.zip asset_content_type: application/zip - + diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9bc76ed4a..a0cfe7a82 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -32,6 +32,8 @@ Fix sample ref-types/wasm-c-api build error with wat2wasm low version Fix zephyr sample build errors Fix source debugger error handling: continue executing when detached Fix scenario where the timeout for atomic wait is set to negative number +Fix link cxx object file error when building wamrc for docker image +Fix XIP issue of handling 64-bit const in 32-bit target ### Enhancements Refactor the layout of interpreter and AOT module instance @@ -75,6 +77,7 @@ Fix warnings in the posix socket implementation Update document for MacOS compilation Install patched LLDB on vscode extension activation Add ARM aeabi memcpy/memmove/memset symbols for AOT bulk memory ops +Enable wasm cache loading in wasm-c-api ### Others Add CIs to release new version and publish binary files diff --git a/core/iwasm/libraries/debug-engine/handler.c b/core/iwasm/libraries/debug-engine/handler.c index cf1a87c59..8d451b1a3 100644 --- a/core/iwasm/libraries/debug-engine/handler.c +++ b/core/iwasm/libraries/debug-engine/handler.c @@ -731,7 +731,7 @@ handle_add_break(WASMGDBServer *server, char *payload) handle_watchpoint_read_add(server, addr, length); break; default: - LOG_ERROR("Unsupported breakpoint type %d", type); + LOG_ERROR("Unsupported breakpoint type %zu", type); write_packet(server, ""); break; } @@ -766,7 +766,7 @@ handle_remove_break(WASMGDBServer *server, char *payload) handle_watchpoint_read_remove(server, addr, length); break; default: - LOG_ERROR("Unsupported breakpoint type %d", type); + LOG_ERROR("Unsupported breakpoint type %zu", type); write_packet(server, ""); break; } diff --git a/test-tools/wamr-ide/README.md b/test-tools/wamr-ide/README.md index 7e2c05ce7..d33f53f78 100644 --- a/test-tools/wamr-ide/README.md +++ b/test-tools/wamr-ide/README.md @@ -46,7 +46,7 @@ From now on, for each release, we have the same version tagged docker image save You could download the tar archive files for docker images from the release, and then load them using the following commands: ```sh -# download the zip or tar.gz from release depending on your platform +# download the zip or tar.gz from release depending on your platform # decompress and get the tar file # on Linux/MacOS, you could use tar @@ -55,14 +55,14 @@ tar xf wasm-debug-server-{version number}.tar.gz # or you could use unzip unzip wasm-toolchain-{version number}.zip unzip wasm-debug-server-{version number}.zip -# load wasm-toolchain +# load wasm-toolchain docker load --input wasm-toolchain.tar # load wasm-debug-server docker load --input wasm-debug-server.tar -# on Windows, you could use any unzip software you like +# on Windows, you could use any unzip software you like # then loading docker images using powershell or git bash -# load wasm-toolchain +# load wasm-toolchain docker load --input ./wasm-toolchain.tar # load wasm-debug-server docker load --input ./wasm-debug-server.tar From e8ce4c542eb914b0ae47986491a303d39af0b960 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Mon, 19 Dec 2022 11:24:46 +0800 Subject: [PATCH 21/33] Implement Multi-tier JIT (#1774) Implement 2-level Multi-tier JIT engine: tier-up from Fast JIT to LLVM JIT to get quick cold startup by Fast JIT and better performance by gradually switching to LLVM JIT when the LLVM JIT functions are compiled by the backend threads. Refer to: https://github.com/bytecodealliance/wasm-micro-runtime/issues/1302 --- build-scripts/config_common.cmake | 18 +- core/iwasm/common/wasm_runtime_common.c | 3 + core/iwasm/compilation/aot.h | 4 + core/iwasm/compilation/aot_emit_function.c | 48 +- .../fast-jit/cg/x86-64/jit_codegen_x86_64.cpp | 923 +++++++++++++++++- core/iwasm/fast-jit/fe/jit_emit_function.c | 4 +- core/iwasm/fast-jit/jit_codecache.c | 3 +- core/iwasm/fast-jit/jit_codegen.h | 11 +- core/iwasm/fast-jit/jit_compiler.c | 147 ++- core/iwasm/fast-jit/jit_compiler.h | 21 +- core/iwasm/fast-jit/jit_frontend.c | 6 + core/iwasm/fast-jit/jit_frontend.h | 11 + core/iwasm/fast-jit/jit_ir.def | 2 +- core/iwasm/interpreter/wasm.h | 71 +- core/iwasm/interpreter/wasm_interp_classic.c | 93 +- core/iwasm/interpreter/wasm_loader.c | 514 +++++++--- core/iwasm/interpreter/wasm_mini_loader.c | 511 +++++++--- core/iwasm/interpreter/wasm_runtime.c | 79 +- core/iwasm/interpreter/wasm_runtime.h | 9 + doc/build_wamr.md | 10 +- tests/wamr-test-suites/test_wamr.sh | 30 +- 21 files changed, 2180 insertions(+), 338 deletions(-) diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 008d7f47c..53b0207fb 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -85,6 +85,13 @@ if (NOT WAMR_BUILD_AOT EQUAL 1) endif () endif () +if (WAMR_BUILD_FAST_JIT EQUAL 1) + if (NOT WAMR_BUILD_LAZY_JIT EQUAL 0) + # Enable Lazy JIT by default + set (WAMR_BUILD_LAZY_JIT 1) + endif () +endif () + if (WAMR_BUILD_JIT EQUAL 1) if (NOT WAMR_BUILD_LAZY_JIT EQUAL 0) # Enable Lazy JIT by default @@ -136,7 +143,12 @@ else () message (" WAMR AOT disabled") endif () if (WAMR_BUILD_FAST_JIT EQUAL 1) - message (" WAMR Fast JIT enabled") + if (WAMR_BUILD_LAZY_JIT EQUAL 1) + add_definitions("-DWASM_ENABLE_LAZY_JIT=1") + message (" WAMR Fast JIT enabled with Lazy Compilation") + else () + message (" WAMR Fast JIT enabled with Eager Compilation") + endif () else () message (" WAMR Fast JIT disabled") endif () @@ -151,6 +163,10 @@ if (WAMR_BUILD_JIT EQUAL 1) else () message (" WAMR LLVM ORC JIT disabled") endif () +if (WAMR_BUILD_FAST_JIT EQUAL 1 AND WAMR_BUILD_JIT EQUAL 1 + AND WAMR_BUILD_LAZY_JIT EQUAL 1) + message (" Multi-tier JIT enabled") +endif () if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) message (" Libc builtin enabled") else () diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 8ddf4af43..fab9b59f8 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -2176,6 +2176,9 @@ static const char *exception_msgs[] = { "wasm auxiliary stack underflow", /* EXCE_AUX_STACK_UNDERFLOW */ "out of bounds table access", /* EXCE_OUT_OF_BOUNDS_TABLE_ACCESS */ "wasm operand stack overflow", /* EXCE_OPERAND_STACK_OVERFLOW */ +#if WASM_ENABLE_FAST_JIT != 0 + "failed to compile fast jit function", /* EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC */ +#endif "", /* EXCE_ALREADY_THROWN */ }; /* clang-format on */ diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index 8e8598c7b..c7989851c 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -309,6 +309,8 @@ aot_get_imp_tbl_data_slots(const AOTImportTable *tbl, bool is_jit_mode) #if WASM_ENABLE_MULTI_MODULE != 0 if (is_jit_mode) return tbl->table_max_size; +#else + (void)is_jit_mode; #endif return tbl->possible_grow ? tbl->table_max_size : tbl->table_init_size; } @@ -319,6 +321,8 @@ aot_get_tbl_data_slots(const AOTTable *tbl, bool is_jit_mode) #if WASM_ENABLE_MULTI_MODULE != 0 if (is_jit_mode) return tbl->table_max_size; +#else + (void)is_jit_mode; #endif return tbl->possible_grow ? tbl->table_max_size : tbl->table_init_size; } diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index fb2fca565..d6a5a4aba 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -798,7 +798,53 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, func = func_ctx->func; } else { - func = func_ctxes[func_idx - import_func_count]->func; + if (!comp_ctx->is_jit_mode) { + func = func_ctxes[func_idx - import_func_count]->func; + } + else { +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) + func = func_ctxes[func_idx - import_func_count]->func; +#else + /* JIT tier-up, load func ptr from func_ptrs[func_idx] */ + LLVMValueRef func_ptr, func_idx_const; + LLVMTypeRef func_ptr_type; + + if (!(func_idx_const = I32_CONST(func_idx))) { + aot_set_last_error("llvm build const failed."); + goto fail; + } + + if (!(func_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->func_ptrs, &func_idx_const, 1, + "func_ptr_tmp"))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } + + if (!(func_ptr = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ptr, "func_ptr"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!(func_ptr_type = LLVMPointerType( + func_ctxes[func_idx - import_func_count] + ->func_type, + 0))) { + aot_set_last_error("construct func ptr type failed."); + goto fail; + } + + if (!(func = LLVMBuildBitCast(comp_ctx->builder, func_ptr, + func_ptr_type, + "indirect_func"))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } +#endif /* end of !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) */ + } } } diff --git a/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp b/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp index d10be6f32..48d1486d5 100644 --- a/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp +++ b/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp @@ -6,6 +6,7 @@ #include "jit_codegen.h" #include "jit_codecache.h" #include "jit_compiler.h" +#include "jit_frontend.h" #include "jit_dump.h" #include @@ -21,6 +22,9 @@ using namespace asmjit; static char *code_block_switch_to_jitted_from_interp = NULL; static char *code_block_return_to_interp_from_jitted = NULL; +#if WASM_ENABLE_LAZY_JIT != 0 +static char *code_block_compile_fast_jit_and_then_call = NULL; +#endif typedef enum { REG_EBP_IDX = 0, @@ -107,16 +111,17 @@ x86::Xmm regs_float[] = { int jit_codegen_interp_jitted_glue(void *exec_env, JitInterpSwitchInfo *info, - void *target) + uint32 func_idx, void *target) { - typedef int32 (*F)(const void *exec_env, void *info, const void *target); + typedef int32 (*F)(const void *exec_env, void *info, uint32 func_idx, + const void *target); union { F f; void *v; } u; u.v = code_block_switch_to_jitted_from_interp; - return u.f(exec_env, info, target); + return u.f(exec_env, info, func_idx, target); } #define PRINT_LINE() LOG_VERBOSE("\n", __LINE__) @@ -5870,6 +5875,7 @@ lower_callbc(JitCompContext *cc, x86::Assembler &a, bh_list *jmp_info_list, JitReg xmm0_f64_hreg = jit_reg_new(JIT_REG_KIND_F64, 0); JitReg ret_reg = *(jit_insn_opnd(insn, 0)); JitReg func_reg = *(jit_insn_opnd(insn, 2)); + JitReg func_idx = *(jit_insn_opnd(insn, 3)); JitReg src_reg; int32 func_reg_no; @@ -5880,6 +5886,15 @@ lower_callbc(JitCompContext *cc, x86::Assembler &a, bh_list *jmp_info_list, func_reg_no = jit_reg_no(func_reg); CHECK_I64_REG_NO(func_reg_no); + CHECK_KIND(func_idx, JIT_REG_KIND_I32); + if (jit_reg_is_const(func_idx)) { + imm.setValue(jit_cc_get_const_I32(cc, func_idx)); + a.mov(regs_i64[REG_RDX_IDX], imm); + } + else { + a.movzx(regs_i64[REG_RDX_IDX], regs_i32[jit_reg_no(func_idx)]); + } + node = (JmpInfo *)jit_malloc(sizeof(JmpInfo)); if (!node) GOTO_FAIL; @@ -6762,6 +6777,761 @@ fail: return return_value; } +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 + +#define MAX_REG_INTS 6 +#define MAX_REG_FLOATS 8 + +void * +jit_codegen_compile_call_to_llvm_jit(const WASMType *func_type) +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + x86::Gp reg_lp = x86::r10, reg_res = x86::r12; + x86::Gp reg_tmp_i64 = x86::r11, reg_tmp_i32 = x86::r11d; + /* the index of integer argument registers */ + uint8 reg_idx_of_int_args[] = { REG_RDI_IDX, REG_RSI_IDX, REG_RDX_IDX, + REG_RCX_IDX, REG_R8_IDX, REG_R9_IDX }; + uint32 n_ints = 0, n_fps = 0, n_stacks = 0, n_pushed; + uint32 int_reg_idx = 0, fp_reg_idx = 0, stack_arg_idx = 0; + uint32 off_to_lp = 0, off_to_res = 0, code_size, i; + uint32 param_count = func_type->param_count; + uint32 result_count = func_type->result_count; + uint32 ext_result_count; + char *code_buf, *stream; + Imm imm; + + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code; + code.init(env); + code.setErrorHandler(&err_handler); + x86::Assembler a(&code); + + /* Load the llvm jit function pointer */ + { + /* r11 = exec_env->module_inst */ + x86::Mem m1(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, module_inst)); + a.mov(reg_tmp_i64, m1); + /* r11 = module_inst->func_ptrs */ + x86::Mem m2(reg_tmp_i64, + (uint32)offsetof(WASMModuleInstance, func_ptrs)); + a.mov(reg_tmp_i64, m2); + /* rax = func_ptrs[func_idx] */ + x86::Mem m3(reg_tmp_i64, x86::rdx, 3, 0); + a.mov(x86::rax, m3); + } + + n_ints++; /* exec_env */ + + for (i = 0; i < param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_I64: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + if (n_ints < MAX_REG_INTS) + n_ints++; + else + n_stacks++; + break; + case VALUE_TYPE_F32: + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS) + n_fps++; + else + n_stacks++; + break; + } + } + + ext_result_count = result_count > 1 ? result_count - 1 : 0; + + if (ext_result_count > 0) { + if (n_ints + ext_result_count <= MAX_REG_INTS) { + /* extra result pointers can be stored into int registers */ + n_ints += ext_result_count; + } + else { + /* part or all extra result pointers must be stored into stack */ + n_stacks += n_ints + ext_result_count - MAX_REG_INTS; + n_ints = MAX_REG_INTS; + } + } + + n_pushed = n_stacks; + if (n_stacks & 1) { + /* Align stack on 16 bytes */ + n_pushed++; + } + if (n_pushed > 0) { + imm.setValue(n_pushed * 8); + a.sub(x86::rsp, imm); + } + + /* r10 = outs_area->lp */ + { + x86::Mem m(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, wasm_stack.s.top)); + a.mov(reg_lp, m); + a.add(reg_lp, (uint32)offsetof(WASMInterpFrame, lp)); + } + + /* rdi = exec_env */ + a.mov(regs_i64[reg_idx_of_int_args[int_reg_idx++]], + regs_i64[hreg_info->exec_env_hreg_index]); + + for (i = 0; i < param_count; i++) { + x86::Mem m_src(reg_lp, off_to_lp); + + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + { + if (int_reg_idx < MAX_REG_INTS) { + a.mov(regs_i32[reg_idx_of_int_args[int_reg_idx]], m_src); + int_reg_idx++; + } + else { + a.mov(reg_tmp_i32, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i32); + stack_arg_idx++; + } + off_to_lp += 4; + break; + } + case VALUE_TYPE_I64: + { + if (int_reg_idx < MAX_REG_INTS) { + a.mov(regs_i64[reg_idx_of_int_args[int_reg_idx]], m_src); + int_reg_idx++; + } + else { + a.mov(reg_tmp_i64, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i64); + stack_arg_idx++; + } + off_to_lp += 8; + break; + } + case VALUE_TYPE_F32: + { + if (fp_reg_idx < MAX_REG_FLOATS) { + a.movss(regs_float[fp_reg_idx], m_src); + fp_reg_idx++; + } + else { + a.mov(reg_tmp_i32, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i32); + stack_arg_idx++; + } + off_to_lp += 4; + break; + } + case VALUE_TYPE_F64: + { + if (fp_reg_idx < MAX_REG_FLOATS) { + a.movsd(regs_float[fp_reg_idx], m_src); + fp_reg_idx++; + } + else { + a.mov(reg_tmp_i64, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i64); + stack_arg_idx++; + } + off_to_lp += 8; + break; + } + } + } + + if (result_count > 0) { + switch (func_type->types[param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_F32: + off_to_res = 4; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + off_to_res = 8; + break; + } + + /* r12 = cur_frame->sp */ + x86::Mem m(x86::rbp, (uint32)offsetof(WASMInterpFrame, sp)); + a.mov(reg_res, m); + + for (i = 0; i < ext_result_count; i++) { + x86::Mem m(reg_res, off_to_res); + + if (int_reg_idx < MAX_REG_INTS) { + a.lea(regs_i64[reg_idx_of_int_args[int_reg_idx]], m); + int_reg_idx++; + } + else { + a.lea(reg_tmp_i64, m); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i64); + stack_arg_idx++; + } + + switch (func_type->types[param_count + 1 + i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_F32: + off_to_res += 4; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + off_to_res += 8; + break; + } + } + } + + bh_assert(int_reg_idx == n_ints); + bh_assert(fp_reg_idx == n_fps); + bh_assert(stack_arg_idx == n_stacks); + + /* Call the llvm jit function */ + a.call(x86::rax); + + /* Check if there was exception thrown */ + { + /* r11 = exec_env->module_inst */ + x86::Mem m1(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, module_inst)); + a.mov(reg_tmp_i64, m1); + /* module_inst->cur_exception */ + x86::Mem m2(reg_tmp_i64, + (uint32)offsetof(WASMModuleInstance, cur_exception)); + /* bl = module_inst->cur_exception[0] */ + a.mov(x86::bl, m2); + + /* cur_exception[0] == 0 ? */ + Imm imm((uint8)0); + a.cmp(x86::bl, imm); + /* If yes, jump to `Get function result and return` */ + imm.setValue(INT32_MAX); + a.je(imm); + + char *stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + /* If no, set eax to JIT_INTERP_ACTION_THROWN, and + jump to code_block_return_to_interp_from_jitted to + return to interpreter */ + imm.setValue(JIT_INTERP_ACTION_THROWN); + a.mov(x86::eax, imm); + imm.setValue(code_block_return_to_interp_from_jitted); + a.mov(x86::rsi, imm); + a.jmp(x86::rsi); + + char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + *(int32 *)(stream - 4) = (uint32)(stream_new - stream); + } + + /* Get function result and return */ + + if (result_count > 0 && func_type->types[param_count] != VALUE_TYPE_F32 + && func_type->types[param_count] != VALUE_TYPE_F64) { + a.mov(x86::rdx, x86::rax); + } + + if (off_to_res > 0) { + imm.setValue(off_to_res); + a.add(reg_res, imm); + /* cur_frame->sp = r12 */ + x86::Mem m(x86::rbp, (uint32)offsetof(WASMInterpFrame, sp)); + a.mov(m, reg_res); + } + + if (n_pushed > 0) { + imm.setValue(n_pushed * 8); + a.add(x86::rsp, imm); + } + + /* Return to the caller */ + { + /* eax = action = JIT_INTERP_ACTION_NORMAL */ + Imm imm(0); + a.mov(x86::eax, imm); + + uint32 jitted_return_addr_offset = + jit_frontend_get_jitted_return_addr_offset(); + x86::Mem m(x86::rbp, jitted_return_addr_offset); + a.jmp(m); + } + + if (err_handler.err) + return NULL; + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + return NULL; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + +#if 0 + dump_native(stream, code_size); +#endif + + return stream; +} + +static WASMInterpFrame * +fast_jit_alloc_frame(WASMExecEnv *exec_env, uint32 param_cell_num, + uint32 ret_cell_num) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + WASMInterpFrame *frame; + uint32 size_frame1 = wasm_interp_interp_frame_size(ret_cell_num); + uint32 size_frame2 = wasm_interp_interp_frame_size(param_cell_num); + + /** + * Check whether we can allocate two frames: the first is an implied + * frame to store the function results from jit function to call, + * the second is the frame for the jit function + */ + if ((uint8 *)exec_env->wasm_stack.s.top + size_frame1 + size_frame2 + > exec_env->wasm_stack.s.top_boundary) { + wasm_set_exception(module_inst, "wasm operand stack overflow"); + return NULL; + } + + /* Allocate the frame */ + frame = (WASMInterpFrame *)exec_env->wasm_stack.s.top; + exec_env->wasm_stack.s.top += size_frame1; + + frame->function = NULL; + frame->ip = NULL; + frame->sp = frame->lp; + frame->prev_frame = wasm_exec_env_get_cur_frame(exec_env); + frame->jitted_return_addr = + (uint8 *)code_block_return_to_interp_from_jitted; + + wasm_exec_env_set_cur_frame(exec_env, frame); + + return frame; +} + +void * +jit_codegen_compile_call_to_fast_jit(const WASMModule *module, uint32 func_idx) +{ + uint32 func_idx_non_import = func_idx - module->import_function_count; + WASMType *func_type = module->functions[func_idx_non_import]->func_type; + /* the index of integer argument registers */ + uint8 reg_idx_of_int_args[] = { REG_RDI_IDX, REG_RSI_IDX, REG_RDX_IDX, + REG_RCX_IDX, REG_R8_IDX, REG_R9_IDX }; + uint32 int_reg_idx, fp_reg_idx, stack_arg_idx; + uint32 switch_info_offset, exec_env_offset, stack_arg_offset; + uint32 int_reg_offset, frame_lp_offset; + uint32 switch_info_size, code_size, i; + uint32 param_count = func_type->param_count; + uint32 result_count = func_type->result_count; + uint32 ext_result_count = result_count > 1 ? result_count - 1 : 0; + uint32 param_cell_num = func_type->param_cell_num; + uint32 ret_cell_num = + func_type->ret_cell_num > 2 ? func_type->ret_cell_num : 2; + char *code_buf, *stream; + Imm imm; + + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code; + code.init(env); + code.setErrorHandler(&err_handler); + x86::Assembler a(&code); + + /** + * Push JitInterpSwitchInfo and make stack 16-byte aligned: + * the size pushed must be odd multiples of 8, as the stack pointer + * %rsp must be aligned to a 16-byte boundary before making a call, + * and when a function (including this llvm jit function) gets + * control, the %rsp is not 16-byte aligned (call instruction will + * push the ret address to stack). + */ + switch_info_size = align_uint((uint32)sizeof(JitInterpSwitchInfo), 16) + 8; + imm.setValue((uint64)switch_info_size); + a.sub(x86::rsp, imm); + + /* Push all integer argument registers since we will use them as + temporarily registers to load/store data */ + for (i = 0; i < MAX_REG_INTS; i++) { + a.push(regs_i64[reg_idx_of_int_args[MAX_REG_INTS - 1 - i]]); + } + + /* We don't push float/double register since we don't use them here */ + + /** + * Layout of the stack now: + * stack arguments + * ret address of the caller + * switch info + * int registers: r9, r8, rcx, rdx, rsi + * exec_env: rdi + */ + + /* offset of the first stack argument to the stack pointer, + add 8 to skip the ret address of the caller */ + stack_arg_offset = switch_info_size + 8 * MAX_REG_INTS + 8; + /* offset of jit interp switch info to the stack pointer */ + switch_info_offset = 8 * MAX_REG_INTS; + /* offset of the first int register to the stack pointer */ + int_reg_offset = 8; + /* offset of exec_env to the stack pointer */ + exec_env_offset = 0; + + /* Call fast_jit_alloc_frame to allocate the stack frame to + receive the results of the fast jit function to call */ + + /* rdi = exec_env, has been already set as exec_env is + the first argument of LLVM JIT function */ + /* rsi = param_cell_num */ + imm.setValue(param_cell_num); + a.mov(x86::rsi, imm); + /* rdx = ret_cell_num */ + imm.setValue(ret_cell_num); + a.mov(x86::rdx, imm); + /* call fast_jit_alloc_frame */ + imm.setValue((uint64)(uintptr_t)fast_jit_alloc_frame); + a.mov(x86::rax, imm); + a.call(x86::rax); + + /* Check the return value, note now rax is the allocated frame */ + { + /* Did fast_jit_alloc_frame return NULL? */ + Imm imm((uint64)0); + a.cmp(x86::rax, imm); + /* If no, jump to `Copy arguments to frame lp area` */ + imm.setValue(INT32_MAX); + a.jne(imm); + + char *stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + /* If yes, set eax to 0, return to caller */ + + /* Pop all integer arument registers */ + for (i = 0; i < MAX_REG_INTS; i++) { + a.pop(regs_i64[reg_idx_of_int_args[i]]); + } + /* Pop jit interp switch info */ + imm.setValue((uint64)switch_info_size); + a.add(x86::rsp, imm); + + /* Return to the caller, don't use leave as we didn't + `push rbp` and `mov rbp, rsp` */ + a.ret(); + + /* Patch the offset of jne instruction */ + char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + *(int32 *)(stream - 4) = (int32)(stream_new - stream); + } + + int_reg_idx = 1; /* skip exec_env */ + fp_reg_idx = 0; + stack_arg_idx = 0; + + /* Offset of the dest arguments to outs area */ + frame_lp_offset = wasm_interp_interp_frame_size(ret_cell_num) + + (uint32)offsetof(WASMInterpFrame, lp); + + /* Copy arguments to frame lp area */ + for (i = 0; i < func_type->param_count; i++) { + x86::Mem m_dst(x86::rax, frame_lp_offset); + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + if (int_reg_idx < MAX_REG_INTS) { + /* Copy i32 argument from int register */ + x86::Mem m_src(x86::rsp, int_reg_offset); + a.mov(x86::esi, m_src); + a.mov(m_dst, x86::esi); + int_reg_offset += 8; + int_reg_idx++; + } + else { + /* Copy i32 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::esi, m_src); + a.mov(m_dst, x86::esi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 4; + break; + case VALUE_TYPE_I64: + if (int_reg_idx < MAX_REG_INTS) { + /* Copy i64 argument from int register */ + x86::Mem m_src(x86::rsp, int_reg_offset); + a.mov(x86::rsi, m_src); + a.mov(m_dst, x86::rsi); + int_reg_offset += 8; + int_reg_idx++; + } + else { + /* Copy i64 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::rsi, m_src); + a.mov(m_dst, x86::rsi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 8; + break; + case VALUE_TYPE_F32: + if (fp_reg_idx < MAX_REG_FLOATS) { + /* Copy f32 argument from fp register */ + a.movss(m_dst, regs_float[fp_reg_idx++]); + } + else { + /* Copy f32 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::esi, m_src); + a.mov(m_dst, x86::esi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 4; + break; + case VALUE_TYPE_F64: + if (fp_reg_idx < MAX_REG_FLOATS) { + /* Copy f64 argument from fp register */ + a.movsd(m_dst, regs_float[fp_reg_idx++]); + } + else { + /* Copy f64 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::rsi, m_src); + a.mov(m_dst, x86::rsi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 8; + break; + default: + bh_assert(0); + } + } + + /* Call the fast jit function */ + { + /* info = rsp + switch_info_offset */ + a.lea(x86::rsi, x86::ptr(x86::rsp, switch_info_offset)); + /* info.frame = frame = rax, or return of fast_jit_alloc_frame */ + x86::Mem m1(x86::rsi, (uint32)offsetof(JitInterpSwitchInfo, frame)); + a.mov(m1, x86::rax); + + /* Call code_block_switch_to_jitted_from_interp + with argument (exec_env, info, func_idx, pc) */ + /* rdi = exec_env */ + a.mov(x86::rdi, x86::ptr(x86::rsp, exec_env_offset)); + /* rsi = info, has been set */ + /* rdx = func_idx */ + imm.setValue(func_idx); + a.mov(x86::rdx, imm); + /* module_inst = exec_env->module_inst */ + a.mov(x86::rcx, + x86::ptr(x86::rdi, (uint32)offsetof(WASMExecEnv, module_inst))); + /* fast_jit_func_ptrs = module_inst->fast_jit_func_ptrs */ + a.mov(x86::rcx, + x86::ptr(x86::rcx, (uint32)offsetof(WASMModuleInstance, + fast_jit_func_ptrs))); + imm.setValue(func_idx_non_import); + a.mov(x86::rax, imm); + x86::Mem m3(x86::rcx, x86::rax, 3, 0); + /* rcx = module_inst->fast_jit_func_ptrs[func_idx_non_import] */ + a.mov(x86::rcx, m3); + + imm.setValue( + (uint64)(uintptr_t)code_block_switch_to_jitted_from_interp); + a.mov(x86::rax, imm); + a.call(x86::rax); + } + + /* No need to check exception thrown here as it will be checked + in the caller */ + + /* Copy function results */ + if (result_count > 0) { + frame_lp_offset = offsetof(WASMInterpFrame, lp); + + switch (func_type->types[param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + a.mov(x86::eax, x86::edx); + frame_lp_offset += 4; + break; + case VALUE_TYPE_I64: + a.mov(x86::rax, x86::rdx); + frame_lp_offset += 8; + break; + case VALUE_TYPE_F32: + /* The first result has been put to xmm0 */ + frame_lp_offset += 4; + break; + case VALUE_TYPE_F64: + /* The first result has been put to xmm0 */ + frame_lp_offset += 8; + break; + default: + bh_assert(0); + } + + /* Copy extra results from exec_env->cur_frame */ + if (ext_result_count > 0) { + /* rdi = exec_env */ + a.mov(x86::rdi, x86::ptr(x86::rsp, exec_env_offset)); + /* rsi = exec_env->cur_frame */ + a.mov(x86::rsi, + x86::ptr(x86::rdi, (uint32)offsetof(WASMExecEnv, cur_frame))); + + for (i = 0; i < ext_result_count; i++) { + switch (func_type->types[param_count + 1 + i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_F32: + { + /* Copy 32-bit result */ + a.mov(x86::ecx, x86::ptr(x86::rsi, frame_lp_offset)); + if (int_reg_idx < MAX_REG_INTS) { + x86::Mem m1(x86::rsp, + exec_env_offset + int_reg_idx * 8); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::ecx); + int_reg_idx++; + } + else { + x86::Mem m1(x86::rsp, stack_arg_offset); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::ecx); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 4; + break; + } + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + { + /* Copy 64-bit result */ + a.mov(x86::rcx, x86::ptr(x86::rsi, frame_lp_offset)); + if (int_reg_idx < MAX_REG_INTS) { + x86::Mem m1(x86::rsp, + exec_env_offset + int_reg_idx * 8); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::rcx); + int_reg_idx++; + } + else { + x86::Mem m1(x86::rsp, stack_arg_offset); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::rcx); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 8; + break; + } + default: + bh_assert(0); + } + } + } + } + + /* Free the frame allocated */ + + /* rdi = exec_env */ + a.mov(x86::rdi, x86::ptr(x86::rsp, exec_env_offset)); + /* rsi = exec_env->cur_frame */ + a.mov(x86::rsi, + x86::ptr(x86::rdi, (uint32)offsetof(WASMExecEnv, cur_frame))); + /* rdx = exec_env->cur_frame->prev_frame */ + a.mov(x86::rdx, + x86::ptr(x86::rsi, (uint32)offsetof(WASMInterpFrame, prev_frame))); + /* exec_env->wasm_stack.s.top = cur_frame */ + { + x86::Mem m(x86::rdi, offsetof(WASMExecEnv, wasm_stack.s.top)); + a.mov(m, x86::rsi); + } + /* exec_env->cur_frame = prev_frame */ + { + x86::Mem m(x86::rdi, offsetof(WASMExecEnv, cur_frame)); + a.mov(m, x86::rdx); + } + + /* Pop all integer arument registers */ + for (i = 0; i < MAX_REG_INTS; i++) { + a.pop(regs_i64[reg_idx_of_int_args[i]]); + } + /* Pop jit interp switch info */ + imm.setValue((uint64)switch_info_size); + a.add(x86::rsp, imm); + + /* Return to the caller, don't use leave as we didn't + `push rbp` and `mov rbp, rsp` */ + a.ret(); + + if (err_handler.err) { + return NULL; + } + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + return NULL; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + +#if 0 + printf("Code of call to fast jit of func %u:\n", func_idx); + dump_native(stream, code_size); + printf("\n"); +#endif + + return stream; +} + +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 */ + bool jit_codegen_lower(JitCompContext *cc) { @@ -6803,6 +7573,8 @@ jit_codegen_init() code.setErrorHandler(&err_handler); x86::Assembler a(&code); + /* Initialize code_block_switch_to_jitted_from_interp */ + /* push callee-save registers */ a.push(x86::rbp); a.push(x86::rbx); @@ -6822,9 +7594,11 @@ jit_codegen_init() /* exec_env_reg = exec_env */ a.mov(regs_i64[hreg_info->exec_env_hreg_index], x86::rdi); /* fp_reg = info->frame */ - a.mov(x86::rbp, x86::ptr(x86::rsi, 0)); - /* jmp target */ - a.jmp(x86::rdx); + a.mov(x86::rbp, x86::ptr(x86::rsi, offsetof(JitInterpSwitchInfo, frame))); + /* rdx = func_idx, is already set in the func_idx argument of + jit_codegen_interp_jitted_glue */ + /* jmp target, rcx = pc */ + a.jmp(x86::rcx); if (err_handler.err) return false; @@ -6842,26 +7616,25 @@ jit_codegen_init() dump_native(stream, code_size); #endif - a.setOffset(0); + /* Initialize code_block_return_to_interp_from_jitted */ - /* TODO: mask floating-point exception */ - /* TODO: floating-point parameters */ + a.setOffset(0); /* pop info */ a.pop(x86::rsi); /* info->frame = fp_reg */ { - x86::Mem m(x86::rsi, 0); + x86::Mem m(x86::rsi, offsetof(JitInterpSwitchInfo, frame)); a.mov(m, x86::rbp); } - /* info->out.ret.ival[0, 1] = rcx */ + /* info->out.ret.ival[0, 1] = rdx */ { - x86::Mem m(x86::rsi, 8); + x86::Mem m(x86::rsi, offsetof(JitInterpSwitchInfo, out.ret.ival)); a.mov(m, x86::rdx); } /* info->out.ret.fval[0, 1] = xmm0 */ { - x86::Mem m(x86::rsi, 16); + x86::Mem m(x86::rsi, offsetof(JitInterpSwitchInfo, out.ret.fval)); a.movsd(m, x86::xmm0); } @@ -6884,12 +7657,125 @@ jit_codegen_init() goto fail1; bh_memcpy_s(stream, code_size, code_buf, code_size); - code_block_return_to_interp_from_jitted = stream; + code_block_return_to_interp_from_jitted = + jit_globals->return_to_interp_from_jitted = stream; + +#if 0 + dump_native(stream, code_size); +#endif + +#if WASM_ENABLE_LAZY_JIT != 0 + /* Initialize code_block_compile_fast_jit_and_then_call */ + + a.setOffset(0); + + /* Use rbx, r12, r13 to save func_dix, module_inst and module, + as they are callee-save registers */ + + /* Backup func_idx: rbx = rdx = func_idx, note that rdx has + been prepared in the caller: + callbc or code_block_switch_to_jitted_from_interp */ + a.mov(x86::rbx, x86::rdx); + /* r12 = module_inst = exec_env->module_inst */ + { + x86::Mem m(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, module_inst)); + a.mov(x86::r12, m); + } + /* rdi = r13 = module_inst->module */ + { + x86::Mem m(x86::r12, (uint32)offsetof(WASMModuleInstance, module)); + a.mov(x86::rdi, m); + a.mov(x86::r13, x86::rdi); + } + /* rsi = rdx = func_idx */ + a.mov(x86::rsi, x86::rdx); + /* Call jit_compiler_compile(module, func_idx) */ + { + Imm imm((uint64)(uintptr_t)jit_compiler_compile); + a.mov(x86::rax, imm); + a.call(x86::rax); + } + + /* Check if failed to compile the jit function */ + { + /* Did jit_compiler_compile return false? */ + Imm imm((uint8)0); + a.cmp(x86::al, imm); + /* If no, jump to `Load compiled func ptr and call it` */ + imm.setValue(INT32_MAX); + a.jne(imm); + + char *stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + /* If yes, call jit_set_exception_with_id to throw exception, + and then set eax to JIT_INTERP_ACTION_THROWN, and jump to + code_block_return_to_interp_from_jitted to return */ + + /* rdi = module_inst */ + a.mov(x86::rdi, x86::r12); + /* rsi = EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC */ + imm.setValue(EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC); + a.mov(x86::rsi, imm); + /* Call jit_set_exception_with_id */ + imm.setValue((uint64)(uintptr_t)jit_set_exception_with_id); + a.mov(x86::rax, imm); + a.call(x86::rax); + /* Return to the caller */ + imm.setValue(JIT_INTERP_ACTION_THROWN); + a.mov(x86::eax, imm); + imm.setValue(code_block_return_to_interp_from_jitted); + a.mov(x86::rsi, imm); + a.jmp(x86::rsi); + + /* Patch the offset of jne instruction */ + char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + *(int32 *)(stream - 4) = (int32)(stream_new - stream); + } + + /* Load compiled func ptr and call it */ + { + /* rsi = module->import_function_count */ + x86::Mem m1(x86::r13, + (uint32)offsetof(WASMModule, import_function_count)); + a.movzx(x86::rsi, m1); + /* rbx = rbx - module->import_function_count */ + a.sub(x86::rbx, x86::rsi); + /* rax = module->fast_jit_func_ptrs */ + x86::Mem m2(x86::r13, (uint32)offsetof(WASMModule, fast_jit_func_ptrs)); + a.mov(x86::rax, m2); + /* rax = fast_jit_func_ptrs[rbx] */ + x86::Mem m3(x86::rax, x86::rbx, 3, 0); + a.mov(x86::rax, m3); + a.jmp(x86::rax); + } + + if (err_handler.err) + goto fail2; + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + goto fail2; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + code_block_compile_fast_jit_and_then_call = + jit_globals->compile_fast_jit_and_then_call = stream; + +#if 0 + dump_native(stream, code_size); +#endif +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 */ - jit_globals->return_to_interp_from_jitted = - code_block_return_to_interp_from_jitted; return true; +#if WASM_ENABLE_LAZY_JIT != 0 +fail2: + jit_code_cache_free(code_block_return_to_interp_from_jitted); +#endif fail1: jit_code_cache_free(code_block_switch_to_jitted_from_interp); return false; @@ -6898,8 +7784,11 @@ fail1: void jit_codegen_destroy() { - jit_code_cache_free(code_block_switch_to_jitted_from_interp); +#if WASM_ENABLE_LAZY_JIT != 0 + jit_code_cache_free(code_block_compile_fast_jit_and_then_call); +#endif jit_code_cache_free(code_block_return_to_interp_from_jitted); + jit_code_cache_free(code_block_switch_to_jitted_from_interp); } /* clang-format off */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_function.c b/core/iwasm/fast-jit/fe/jit_emit_function.c index ce22a950a..a89f2ff59 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_function.c +++ b/core/iwasm/fast-jit/fe/jit_emit_function.c @@ -409,7 +409,7 @@ jit_compile_op_call(JitCompContext *cc, uint32 func_idx, bool tail_call) res = create_first_res_reg(cc, func_type); - GEN_INSN(CALLBC, res, 0, jitted_code); + GEN_INSN(CALLBC, res, 0, jitted_code, NEW_CONST(I32, func_idx)); if (!post_return(cc, func_type, res, true)) { goto fail; @@ -700,7 +700,7 @@ jit_compile_op_call_indirect(JitCompContext *cc, uint32 type_idx, goto fail; } } - GEN_INSN(CALLBC, res, 0, jitted_code); + GEN_INSN(CALLBC, res, 0, jitted_code, func_idx); /* Store res into current frame, so that post_return in block func_return can get the value */ n = cc->jit_frame->sp - cc->jit_frame->lp; diff --git a/core/iwasm/fast-jit/jit_codecache.c b/core/iwasm/fast-jit/jit_codecache.c index 4c899ad9d..66c2d033a 100644 --- a/core/iwasm/fast-jit/jit_codecache.c +++ b/core/iwasm/fast-jit/jit_codecache.c @@ -58,8 +58,7 @@ jit_pass_register_jitted_code(JitCompContext *cc) { uint32 jit_func_idx = cc->cur_wasm_func_idx - cc->cur_wasm_module->import_function_count; - cc->cur_wasm_func->fast_jit_jitted_code = cc->jitted_addr_begin; cc->cur_wasm_module->fast_jit_func_ptrs[jit_func_idx] = - cc->jitted_addr_begin; + cc->cur_wasm_func->fast_jit_jitted_code = cc->jitted_addr_begin; return true; } diff --git a/core/iwasm/fast-jit/jit_codegen.h b/core/iwasm/fast-jit/jit_codegen.h index 666a239a6..735cddab6 100644 --- a/core/iwasm/fast-jit/jit_codegen.h +++ b/core/iwasm/fast-jit/jit_codegen.h @@ -65,6 +65,14 @@ jit_codegen_gen_native(JitCompContext *cc); bool jit_codegen_lower(JitCompContext *cc); +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 +void * +jit_codegen_compile_call_to_llvm_jit(const WASMType *func_type); + +void * +jit_codegen_compile_call_to_fast_jit(const WASMModule *module, uint32 func_idx); +#endif + /** * Dump native code in the given range to assembly. * @@ -75,7 +83,8 @@ void jit_codegen_dump_native(void *begin_addr, void *end_addr); int -jit_codegen_interp_jitted_glue(void *self, JitInterpSwitchInfo *info, void *pc); +jit_codegen_interp_jitted_glue(void *self, JitInterpSwitchInfo *info, + uint32 func_idx, void *pc); #ifdef __cplusplus } diff --git a/core/iwasm/fast-jit/jit_compiler.c b/core/iwasm/fast-jit/jit_compiler.c index c10a40994..67dcb7b51 100644 --- a/core/iwasm/fast-jit/jit_compiler.c +++ b/core/iwasm/fast-jit/jit_compiler.c @@ -10,9 +10,9 @@ #include "../interpreter/wasm.h" typedef struct JitCompilerPass { - /* Name of the pass. */ + /* Name of the pass */ const char *name; - /* The entry of the compiler pass. */ + /* The entry of the compiler pass */ bool (*run)(JitCompContext *cc); } JitCompilerPass; @@ -30,7 +30,7 @@ static JitCompilerPass compiler_passes[] = { #undef REG_PASS }; -/* Number of compiler passes. */ +/* Number of compiler passes */ #define COMPILER_PASS_NUM (sizeof(compiler_passes) / sizeof(compiler_passes[0])) #if WASM_ENABLE_FAST_JIT_DUMP == 0 @@ -43,14 +43,17 @@ static const uint8 compiler_passes_with_dump[] = { }; #endif -/* The exported global data of JIT compiler. */ +/* The exported global data of JIT compiler */ static JitGlobals jit_globals = { #if WASM_ENABLE_FAST_JIT_DUMP == 0 .passes = compiler_passes_without_dump, #else .passes = compiler_passes_with_dump, #endif - .return_to_interp_from_jitted = NULL + .return_to_interp_from_jitted = NULL, +#if WASM_ENABLE_LAZY_JIT != 0 + .compile_fast_jit_and_then_call = NULL, +#endif }; /* clang-format on */ @@ -60,7 +63,7 @@ apply_compiler_passes(JitCompContext *cc) const uint8 *p = jit_globals.passes; for (; *p; p++) { - /* Set the pass NO. */ + /* Set the pass NO */ cc->cur_pass_no = p - jit_globals.passes; bh_assert(*p < COMPILER_PASS_NUM); @@ -120,37 +123,53 @@ jit_compiler_get_pass_name(unsigned i) bool jit_compiler_compile(WASMModule *module, uint32 func_idx) { - JitCompContext *cc; + JitCompContext *cc = NULL; char *last_error; - bool ret = true; + bool ret = false; + uint32 i = func_idx - module->import_function_count; + uint32 j = i % WASM_ORC_JIT_BACKEND_THREAD_NUM; - /* Initialize compilation context. */ - if (!(cc = jit_calloc(sizeof(*cc)))) - return false; + /* Lock to avoid duplicated compilation by other threads */ + os_mutex_lock(&module->fast_jit_thread_locks[j]); + + if (jit_compiler_is_compiled(module, func_idx)) { + /* Function has been compiled */ + os_mutex_unlock(&module->fast_jit_thread_locks[j]); + return true; + } + + /* Initialize the compilation context */ + if (!(cc = jit_calloc(sizeof(*cc)))) { + goto fail; + } if (!jit_cc_init(cc, 64)) { - jit_free(cc); - return false; + goto fail; } cc->cur_wasm_module = module; - cc->cur_wasm_func = - module->functions[func_idx - module->import_function_count]; + cc->cur_wasm_func = module->functions[i]; cc->cur_wasm_func_idx = func_idx; cc->mem_space_unchanged = (!cc->cur_wasm_func->has_op_memory_grow && !cc->cur_wasm_func->has_op_func_call) || (!module->possible_memory_grow); - /* Apply compiler passes. */ + /* Apply compiler passes */ if (!apply_compiler_passes(cc) || jit_get_last_error(cc)) { last_error = jit_get_last_error(cc); os_printf("fast jit compilation failed: %s\n", last_error ? last_error : "unknown error"); - ret = false; + goto fail; } - /* Delete the compilation context. */ - jit_cc_delete(cc); + ret = true; + +fail: + /* Destroy the compilation context */ + if (cc) + jit_cc_delete(cc); + + os_mutex_unlock(&module->fast_jit_thread_locks[j]); return ret; } @@ -169,8 +188,92 @@ jit_compiler_compile_all(WASMModule *module) return true; } -int -jit_interp_switch_to_jitted(void *exec_env, JitInterpSwitchInfo *info, void *pc) +bool +jit_compiler_is_compiled(const WASMModule *module, uint32 func_idx) { - return jit_codegen_interp_jitted_glue(exec_env, info, pc); + uint32 i = func_idx - module->import_function_count; + + bh_assert(func_idx >= module->import_function_count + && func_idx + < module->import_function_count + module->function_count); + +#if WASM_ENABLE_LAZY_JIT == 0 + return module->fast_jit_func_ptrs[i] ? true : false; +#else + return module->fast_jit_func_ptrs[i] + != jit_globals.compile_fast_jit_and_then_call + ? true + : false; +#endif +} + +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 +bool +jit_compiler_set_call_to_llvm_jit(WASMModule *module, uint32 func_idx) +{ + uint32 i = func_idx - module->import_function_count; + uint32 j = i % WASM_ORC_JIT_BACKEND_THREAD_NUM; + WASMType *func_type = module->functions[i]->func_type; + uint32 k = + ((uint32)(uintptr_t)func_type >> 3) % WASM_ORC_JIT_BACKEND_THREAD_NUM; + void *func_ptr = NULL; + + /* Compile code block of call_to_llvm_jit_from_fast_jit of + this kind of function type if it hasn't been compiled */ + if (!(func_ptr = func_type->call_to_llvm_jit_from_fast_jit)) { + os_mutex_lock(&module->fast_jit_thread_locks[k]); + if (!(func_ptr = func_type->call_to_llvm_jit_from_fast_jit)) { + if (!(func_ptr = func_type->call_to_llvm_jit_from_fast_jit = + jit_codegen_compile_call_to_llvm_jit(func_type))) { + os_mutex_unlock(&module->fast_jit_thread_locks[k]); + return false; + } + } + os_mutex_unlock(&module->fast_jit_thread_locks[k]); + } + + /* Switch current fast jit func ptr to the code block */ + os_mutex_lock(&module->fast_jit_thread_locks[j]); + module->fast_jit_func_ptrs[i] = func_ptr; + os_mutex_unlock(&module->fast_jit_thread_locks[j]); + return true; +} + +bool +jit_compiler_set_call_to_fast_jit(WASMModule *module, uint32 func_idx) +{ + void *func_ptr = NULL; + + func_ptr = jit_codegen_compile_call_to_fast_jit(module, func_idx); + if (func_ptr) { + jit_compiler_set_llvm_jit_func_ptr(module, func_idx, func_ptr); + } + + return func_ptr ? true : false; +} + +void +jit_compiler_set_llvm_jit_func_ptr(WASMModule *module, uint32 func_idx, + void *func_ptr) +{ + WASMModuleInstance *instance; + uint32 i = func_idx - module->import_function_count; + + module->functions[i]->llvm_jit_func_ptr = module->func_ptrs[i] = func_ptr; + + os_mutex_lock(&module->instance_list_lock); + instance = module->instance_list; + while (instance) { + instance->func_ptrs[func_idx] = func_ptr; + instance = instance->e->next; + } + os_mutex_unlock(&module->instance_list_lock); +} +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 */ + +int +jit_interp_switch_to_jitted(void *exec_env, JitInterpSwitchInfo *info, + uint32 func_idx, void *pc) +{ + return jit_codegen_interp_jitted_glue(exec_env, info, func_idx, pc); } diff --git a/core/iwasm/fast-jit/jit_compiler.h b/core/iwasm/fast-jit/jit_compiler.h index 602494db9..9a49cffdd 100644 --- a/core/iwasm/fast-jit/jit_compiler.h +++ b/core/iwasm/fast-jit/jit_compiler.h @@ -18,6 +18,9 @@ typedef struct JitGlobals { /* Compiler pass sequence, the last element must be 0 */ const uint8 *passes; char *return_to_interp_from_jitted; +#if WASM_ENABLE_LAZY_JIT != 0 + char *compile_fast_jit_and_then_call; +#endif } JitGlobals; /** @@ -87,8 +90,24 @@ jit_compiler_compile(WASMModule *module, uint32 func_idx); bool jit_compiler_compile_all(WASMModule *module); +bool +jit_compiler_is_compiled(const WASMModule *module, uint32 func_idx); + +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 +bool +jit_compiler_set_call_to_llvm_jit(WASMModule *module, uint32 func_idx); + +bool +jit_compiler_set_call_to_fast_jit(WASMModule *module, uint32 func_idx); + +void +jit_compiler_set_llvm_jit_func_ptr(WASMModule *module, uint32 func_idx, + void *func_ptr); +#endif + int -jit_interp_switch_to_jitted(void *self, JitInterpSwitchInfo *info, void *pc); +jit_interp_switch_to_jitted(void *self, JitInterpSwitchInfo *info, + uint32 func_idx, void *pc); /* * Pass declarations: diff --git a/core/iwasm/fast-jit/jit_frontend.c b/core/iwasm/fast-jit/jit_frontend.c index 995bfb80a..b20be9609 100644 --- a/core/iwasm/fast-jit/jit_frontend.c +++ b/core/iwasm/fast-jit/jit_frontend.c @@ -2255,6 +2255,12 @@ jit_frontend_translate_func(JitCompContext *cc) return basic_block_entry; } +uint32 +jit_frontend_get_jitted_return_addr_offset() +{ + return (uint32)offsetof(WASMInterpFrame, jitted_return_addr); +} + #if 0 #if WASM_ENABLE_THREAD_MGR != 0 bool diff --git a/core/iwasm/fast-jit/jit_frontend.h b/core/iwasm/fast-jit/jit_frontend.h index d706c90b8..fce8ecfd2 100644 --- a/core/iwasm/fast-jit/jit_frontend.h +++ b/core/iwasm/fast-jit/jit_frontend.h @@ -13,6 +13,10 @@ #include "../aot/aot_runtime.h" #endif +#ifdef __cplusplus +extern "C" { +#endif + #if WASM_ENABLE_AOT == 0 typedef enum IntCond { INT_EQZ = 0, @@ -143,6 +147,9 @@ jit_frontend_translate_func(JitCompContext *cc); bool jit_frontend_lower(JitCompContext *cc); +uint32 +jit_frontend_get_jitted_return_addr_offset(); + uint32 jit_frontend_get_global_data_offset(const WASMModule *module, uint32 global_idx); @@ -483,4 +490,8 @@ set_local_f64(JitFrame *frame, int n, JitReg val) #define PUSH_FUNCREF(v) PUSH(v, VALUE_TYPE_FUNCREF) #define PUSH_EXTERNREF(v) PUSH(v, VALUE_TYPE_EXTERNREF) +#ifdef __cplusplus +} +#endif + #endif diff --git a/core/iwasm/fast-jit/jit_ir.def b/core/iwasm/fast-jit/jit_ir.def index d16843328..8a4396da5 100644 --- a/core/iwasm/fast-jit/jit_ir.def +++ b/core/iwasm/fast-jit/jit_ir.def @@ -196,7 +196,7 @@ INSN(LOOKUPSWITCH, LookupSwitch, 1, 0) /* Call and return instructions */ INSN(CALLNATIVE, VReg, 2, 1) -INSN(CALLBC, Reg, 3, 2) +INSN(CALLBC, Reg, 4, 2) INSN(RETURNBC, Reg, 3, 0) INSN(RETURN, Reg, 1, 0) diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 58305a7f4..7efa6d5ff 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -124,6 +124,12 @@ typedef struct WASMType { uint16 param_cell_num; uint16 ret_cell_num; uint16 ref_count; +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* Code block to call llvm jit functions of this + kind of function type from fast jit jitted code */ + void *call_to_llvm_jit_from_fast_jit; +#endif /* types of params and results */ uint8 types[1]; } WASMType; @@ -255,13 +261,6 @@ struct WASMFunction { uint32 const_cell_num; #endif -#if WASM_ENABLE_FAST_JIT != 0 - void *fast_jit_jitted_code; -#endif -#if WASM_ENABLE_JIT != 0 - void *llvm_jit_func_ptr; -#endif - #if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ || WASM_ENABLE_WAMR_COMPILER != 0 /* Whether function has opcode memory.grow */ @@ -277,6 +276,13 @@ struct WASMFunction { /* Whether function has opcode set_global_aux_stack */ bool has_op_set_global_aux_stack; #endif + +#if WASM_ENABLE_FAST_JIT != 0 + void *fast_jit_jitted_code; +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + void *llvm_jit_func_ptr; +#endif +#endif }; struct WASMGlobal { @@ -377,18 +383,22 @@ typedef struct WASMCustomSection { } WASMCustomSection; #endif -#if WASM_ENABLE_JIT != 0 +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 struct AOTCompData; struct AOTCompContext; /* Orc JIT thread arguments */ typedef struct OrcJitThreadArg { +#if WASM_ENABLE_JIT != 0 struct AOTCompContext *comp_ctx; +#endif struct WASMModule *module; uint32 group_idx; } OrcJitThreadArg; #endif +struct WASMModuleInstance; + struct WASMModule { /* Module type, for module loaded from WASM bytecode binary, this field is Wasm_Module_Bytecode; @@ -495,18 +505,22 @@ struct WASMModule { uint64 load_size; #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ + && WASM_ENABLE_LAZY_JIT != 0) /** - * Count how many instances reference this module. When source - * debugging feature enabled, the debugger may modify the code - * section of the module, so we need to report a warning if user - * create several instances based on the same module + * List of instances referred to this module. When source debugging + * feature is enabled, the debugger may modify the code section of + * the module, so we need to report a warning if user create several + * instances based on the same module. Sub instances created by + * lib-pthread or spawn API won't be added into the list. * - * Sub_instances created by lib-pthread or spawn API will not - * influence or check the ref count + * Also add the instance to the list for Fast JIT to LLVM JIT + * tier-up, since we need to lazily update the LLVM func pointers + * in the instance. */ - uint32 ref_count; - korp_mutex ref_count_lock; + struct WASMModuleInstance *instance_list; + korp_mutex instance_list_lock; #endif #if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 @@ -521,6 +535,9 @@ struct WASMModule { #if WASM_ENABLE_FAST_JIT != 0 /* func pointers of Fast JITed (un-imported) functions */ void **fast_jit_func_ptrs; + /* locks for Fast JIT lazy compilation */ + korp_mutex fast_jit_thread_locks[WASM_ORC_JIT_BACKEND_THREAD_NUM]; + bool fast_jit_thread_locks_inited[WASM_ORC_JIT_BACKEND_THREAD_NUM]; #endif #if WASM_ENABLE_JIT != 0 @@ -530,9 +547,27 @@ struct WASMModule { void **func_ptrs; /* whether the func pointers are compiled */ bool *func_ptrs_compiled; - bool orcjit_stop_compiling; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* backend compilation threads */ korp_tid orcjit_threads[WASM_ORC_JIT_BACKEND_THREAD_NUM]; + /* backend thread arguments */ OrcJitThreadArg orcjit_thread_args[WASM_ORC_JIT_BACKEND_THREAD_NUM]; + /* whether to stop the compilation of backend threads */ + bool orcjit_stop_compiling; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* wait lock/cond for the synchronization of + the llvm jit initialization */ + korp_mutex tierup_wait_lock; + korp_cond tierup_wait_cond; + bool tierup_wait_lock_inited; + korp_tid llvm_jit_init_thread; + /* whether the llvm jit is initialized */ + bool llvm_jit_inited; #endif }; diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 5fe814384..fa542b14e 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -3947,28 +3947,48 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #if WASM_ENABLE_FAST_JIT != 0 static void -fast_jit_call_func_bytecode(WASMExecEnv *exec_env, +fast_jit_call_func_bytecode(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, WASMFunctionInstance *function, WASMInterpFrame *frame) { JitGlobals *jit_globals = jit_compiler_get_jit_globals(); JitInterpSwitchInfo info; + WASMModule *module = module_inst->module; WASMType *func_type = function->u.func->func_type; uint8 type = func_type->result_count ? func_type->types[func_type->param_count] : VALUE_TYPE_VOID; + uint32 func_idx = (uint32)(function - module_inst->e->functions); + uint32 func_idx_non_import = func_idx - module->import_function_count; + int32 action; #if WASM_ENABLE_REF_TYPES != 0 if (type == VALUE_TYPE_EXTERNREF || type == VALUE_TYPE_FUNCREF) type = VALUE_TYPE_I32; #endif +#if WASM_ENABLE_LAZY_JIT != 0 + if (!jit_compiler_compile(module, func_idx)) { + wasm_set_exception(module_inst, "failed to compile fast jit function"); + return; + } +#endif + bh_assert(jit_compiler_is_compiled(module, func_idx)); + + /* Switch to jitted code to call the jit function */ info.out.ret.last_return_type = type; info.frame = frame; frame->jitted_return_addr = (uint8 *)jit_globals->return_to_interp_from_jitted; - jit_interp_switch_to_jitted(exec_env, &info, - function->u.func->fast_jit_jitted_code); + action = jit_interp_switch_to_jitted( + exec_env, &info, func_idx, + module_inst->fast_jit_func_ptrs[func_idx_non_import]); + bh_assert(action == JIT_INTERP_ACTION_NORMAL + || (action == JIT_INTERP_ACTION_THROWN + && wasm_runtime_get_exception(exec_env->module_inst))); + + /* Get the return values form info.out.ret */ if (func_type->result_count) { switch (type) { case VALUE_TYPE_I32: @@ -3992,8 +4012,10 @@ fast_jit_call_func_bytecode(WASMExecEnv *exec_env, break; } } + (void)action; + (void)func_idx; } -#endif +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ #if WASM_ENABLE_JIT != 0 static bool @@ -4023,11 +4045,13 @@ llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, WASMType *func_type = function->u.func->func_type; uint32 result_count = func_type->result_count; uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + uint32 func_idx = (uint32)(function - module_inst->e->functions); bool ret; #if (WASM_ENABLE_DUMP_CALL_STACK != 0) || (WASM_ENABLE_PERF_PROFILING != 0) if (!llvm_jit_alloc_frame(exec_env, function - module_inst->e->functions)) { - wasm_set_exception(module_inst, "wasm operand stack overflow"); + /* wasm operand stack overflow has been thrown, + no need to throw again */ return false; } #endif @@ -4069,8 +4093,8 @@ llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, } ret = wasm_runtime_invoke_native( - exec_env, function->u.func->llvm_jit_func_ptr, func_type, NULL, - NULL, argv1, argc, argv); + exec_env, module_inst->func_ptrs[func_idx], func_type, NULL, NULL, + argv1, argc, argv); if (!ret || wasm_get_exception(module_inst)) { if (clear_wasi_proc_exit_exception(module_inst)) @@ -4120,8 +4144,8 @@ llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, } else { ret = wasm_runtime_invoke_native( - exec_env, function->u.func->llvm_jit_func_ptr, func_type, NULL, - NULL, argv, argc, argv); + exec_env, module_inst->func_ptrs[func_idx], func_type, NULL, NULL, + argv, argc, argv); if (clear_wasi_proc_exit_exception(module_inst)) ret = true; @@ -4129,7 +4153,7 @@ llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, return ret && !wasm_get_exception(module_inst) ? true : false; } } -#endif +#endif /* end of WASM_ENABLE_JIT != 0 */ void wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, @@ -4202,18 +4226,63 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, } } else { -#if WASM_ENABLE_JIT != 0 +#if WASM_ENABLE_LAZY_JIT != 0 + + /* Fast JIT to LLVM JIT tier-up is enabled */ +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 + /* Fast JIT and LLVM JIT are both enabled, call llvm jit function + if it is compiled, else call fast jit function */ + uint32 func_idx = (uint32)(function - module_inst->e->functions); + if (module_inst->module->func_ptrs_compiled + [func_idx - module_inst->module->import_function_count]) { + llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, + argv); + /* For llvm jit, the results have been stored in argv, + no need to copy them from stack frame again */ + copy_argv_from_frame = false; + } + else { + fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); + } +#elif WASM_ENABLE_JIT != 0 + /* Only LLVM JIT is enabled */ llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, argv); /* For llvm jit, the results have been stored in argv, no need to copy them from stack frame again */ copy_argv_from_frame = false; #elif WASM_ENABLE_FAST_JIT != 0 - fast_jit_call_func_bytecode(exec_env, function, frame); + /* Only Fast JIT is enabled */ + fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); #else + /* Both Fast JIT and LLVM JIT are disabled */ wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); #endif + +#else /* else of WASM_ENABLE_LAZY_JIT != 0 */ + + /* Fast JIT to LLVM JIT tier-up is enabled */ +#if WASM_ENABLE_JIT != 0 + /* LLVM JIT is enabled */ + llvm_jit_call_func_bytecode(module_inst, exec_env, function, argc, + argv); + /* For llvm jit, the results have been stored in argv, + no need to copy them from stack frame again */ + copy_argv_from_frame = false; +#elif WASM_ENABLE_FAST_JIT != 0 + /* Fast JIT is enabled */ + fast_jit_call_func_bytecode(module_inst, exec_env, function, frame); +#else + /* Both Fast JIT and LLVM JIT are disabled */ + wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); +#endif + +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 */ + (void)wasm_interp_call_func_bytecode; +#if WASM_ENABLE_FAST_JIT != 0 + (void)fast_jit_call_func_bytecode; +#endif } /* Output the return value to the caller */ diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index a74780d9d..ef7df8e00 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -428,6 +428,12 @@ destroy_wasm_type(WASMType *type) return; } +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + if (type->call_to_llvm_jit_from_fast_jit) + jit_code_cache_free(type->call_to_llvm_jit_from_fast_jit); +#endif + wasm_runtime_free(type); } @@ -2925,93 +2931,78 @@ calculate_global_data_offset(WASMModule *module) module->global_data_size = data_offset; } -#if WASM_ENABLE_JIT != 0 -static void * -orcjit_thread_callback(void *arg) -{ - LLVMOrcJITTargetAddress func_addr = 0; - OrcJitThreadArg *thread_arg = (OrcJitThreadArg *)arg; - AOTCompContext *comp_ctx = thread_arg->comp_ctx; - WASMModule *module = thread_arg->module; - uint32 group_idx = thread_arg->group_idx; - uint32 group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; - uint32 func_count = module->function_count; - uint32 i, j; - typedef void (*F)(void); - LLVMErrorRef error; - char func_name[48]; - union { - F f; - void *v; - } u; - - /* Compile jit functions of this group */ - for (i = group_idx; i < func_count; - i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { - snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, i, - "_wrapper"); - LOG_DEBUG("compile func %s", func_name); - error = - LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, func_name); - if (error != LLVMErrorSuccess) { - char *err_msg = LLVMGetErrorMessage(error); - os_printf("failed to compile orc jit function: %s\n", err_msg); - LLVMDisposeErrorMessage(err_msg); - continue; - } - - /* Call the jit wrapper function to trigger its compilation, so as - to compile the actual jit functions, since we add the latter to - function list in the PartitionFunction callback */ - u.v = (void *)func_addr; - u.f(); - - for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { - if (i + j * group_stride < func_count) - module->func_ptrs_compiled[i + j * group_stride] = true; - } - - if (module->orcjit_stop_compiling) { - break; - } - } - - return NULL; -} - -static void -orcjit_stop_compile_threads(WASMModule *module) -{ - uint32 i, thread_num = (uint32)(sizeof(module->orcjit_thread_args) - / sizeof(OrcJitThreadArg)); - - module->orcjit_stop_compiling = true; - for (i = 0; i < thread_num; i++) { - if (module->orcjit_threads[i]) - os_thread_join(module->orcjit_threads[i], NULL); - } -} - +#if WASM_ENABLE_FAST_JIT != 0 static bool -compile_llvm_jit_functions(WASMModule *module, char *error_buf, - uint32 error_buf_size) +init_fast_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_LAZY_JIT != 0 + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); +#endif + uint32 i; + + if (!module->function_count) + return true; + + if (!(module->fast_jit_func_ptrs = + loader_malloc(sizeof(void *) * module->function_count, error_buf, + error_buf_size))) { + return false; + } + +#if WASM_ENABLE_LAZY_JIT != 0 + for (i = 0; i < module->function_count; i++) { + module->fast_jit_func_ptrs[i] = + jit_globals->compile_fast_jit_and_then_call; + } +#endif + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (os_mutex_init(&module->fast_jit_thread_locks[i]) != 0) { + set_error_buf(error_buf, error_buf_size, + "init fast jit thread lock failed"); + return false; + } + module->fast_jit_thread_locks_inited[i] = true; + } + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ + +#if WASM_ENABLE_JIT != 0 +static bool +init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, + uint32 error_buf_size) { AOTCompOption option = { 0 }; char *aot_last_error; uint64 size; - uint32 thread_num, i; - if (module->function_count > 0) { - size = sizeof(void *) * (uint64)module->function_count - + sizeof(bool) * (uint64)module->function_count; - if (!(module->func_ptrs = - loader_malloc(size, error_buf, error_buf_size))) { - return false; - } - module->func_ptrs_compiled = - (bool *)((uint8 *)module->func_ptrs - + sizeof(void *) * module->function_count); + if (module->function_count == 0) + return true; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LLVM_JIT != 0 + if (os_mutex_init(&module->tierup_wait_lock) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup lock failed"); + return false; } + if (os_cond_init(&module->tierup_wait_cond) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup cond failed"); + os_mutex_destroy(&module->tierup_wait_lock); + return false; + } + module->tierup_wait_lock_inited = true; +#endif + + size = sizeof(void *) * (uint64)module->function_count + + sizeof(bool) * (uint64)module->function_count; + if (!(module->func_ptrs = loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + module->func_ptrs_compiled = + (bool *)((uint8 *)module->func_ptrs + + sizeof(void *) * module->function_count); module->comp_data = aot_create_comp_data(module); if (!module->comp_data) { @@ -3052,6 +3043,19 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, return false; } + return true; +} + +static bool +init_llvm_jit_functions_stage2(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + char *aot_last_error; + uint32 i; + + if (module->function_count == 0) + return true; + if (!aot_compile_wasm(module->comp_ctx)) { aot_last_error = aot_get_last_error(); bh_assert(aot_last_error != NULL); @@ -3059,7 +3063,12 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, return false; } - bh_print_time("Begin to lookup jit functions"); +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->orcjit_stop_compiling) + return false; +#endif + + bh_print_time("Begin to lookup llvm jit functions"); for (i = 0; i < module->function_count; i++) { LLVMOrcJITTargetAddress func_addr = 0; @@ -3072,7 +3081,7 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, if (error != LLVMErrorSuccess) { char *err_msg = LLVMGetErrorMessage(error); set_error_buf_v(error_buf, error_buf_size, - "failed to compile orc jit function: %s", err_msg); + "failed to compile llvm jit function: %s", err_msg); LLVMDisposeErrorMessage(err_msg); return false; } @@ -3084,17 +3093,211 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, * loading/storing at the same time. */ module->func_ptrs[i] = (void *)func_addr; - module->functions[i]->llvm_jit_func_ptr = (void *)func_addr; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->orcjit_stop_compiling) + return false; +#endif } + bh_print_time("End lookup llvm jit functions"); + + return true; +} +#endif /* end of WASM_ENABLE_JIT != 0 */ + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 +static void * +init_llvm_jit_functions_stage2_callback(void *arg) +{ + WASMModule *module = (WASMModule *)arg; + char error_buf[128]; + uint32 error_buf_size = (uint32)sizeof(error_buf); + + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + module->orcjit_stop_compiling = true; + return NULL; + } + + os_mutex_lock(&module->tierup_wait_lock); + module->llvm_jit_inited = true; + os_cond_broadcast(&module->tierup_wait_cond); + os_mutex_unlock(&module->tierup_wait_lock); + + return NULL; +} +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 +/* The callback function to compile jit functions */ +static void * +orcjit_thread_callback(void *arg) +{ + OrcJitThreadArg *thread_arg = (OrcJitThreadArg *)arg; +#if WASM_ENABLE_JIT != 0 + AOTCompContext *comp_ctx = thread_arg->comp_ctx; +#endif + WASMModule *module = thread_arg->module; + uint32 group_idx = thread_arg->group_idx; + uint32 group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; + uint32 func_count = module->function_count; + uint32 i; + +#if WASM_ENABLE_FAST_JIT != 0 + /* Compile fast jit funcitons of this group */ + for (i = group_idx; i < func_count; i += group_stride) { + if (!jit_compiler_compile(module, i + module->import_function_count)) { + os_printf("failed to compile fast jit function %u\n", i); + break; + } + + if (module->orcjit_stop_compiling) { + return NULL; + } + } +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* For JIT tier-up, set each llvm jit func to call_to_fast_jit */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + uint32 j; + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + if (!jit_compiler_set_call_to_fast_jit( + module, + i + j * group_stride + module->import_function_count)) { + os_printf( + "failed to compile call_to_fast_jit for func %u\n", + i + j * group_stride + module->import_function_count); + module->orcjit_stop_compiling = true; + return NULL; + } + } + if (module->orcjit_stop_compiling) { + return NULL; + } + } + } + + /* Wait until init_llvm_jit_functions_stage2 finishes */ + os_mutex_lock(&module->tierup_wait_lock); + while (!module->llvm_jit_inited) { + os_cond_reltimedwait(&module->tierup_wait_cond, + &module->tierup_wait_lock, 10); + if (module->orcjit_stop_compiling) { + /* init_llvm_jit_functions_stage2 failed */ + os_mutex_unlock(&module->tierup_wait_lock); + return NULL; + } + } + os_mutex_unlock(&module->tierup_wait_lock); +#endif + +#if WASM_ENABLE_JIT != 0 + /* Compile llvm jit functions of this group */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + LLVMOrcJITTargetAddress func_addr = 0; + LLVMErrorRef error; + char func_name[48]; + typedef void (*F)(void); + union { + F f; + void *v; + } u; + uint32 j; + + snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, i, + "_wrapper"); + LOG_DEBUG("compile llvm jit func %s", func_name); + error = + LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + os_printf("failed to compile llvm jit function %u: %s", i, err_msg); + LLVMDisposeErrorMessage(err_msg); + break; + } + + /* Call the jit wrapper function to trigger its compilation, so as + to compile the actual jit functions, since we add the latter to + function list in the PartitionFunction callback */ + u.v = (void *)func_addr; + u.f(); + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + module->func_ptrs_compiled[i + j * group_stride] = true; +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, + i + j * group_stride); + error = LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, + func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + os_printf("failed to compile llvm jit function %u: %s", i, + err_msg); + LLVMDisposeErrorMessage(err_msg); + /* Ignore current llvm jit func, as its func ptr is + previous set to call_to_fast_jit, which also works */ + continue; + } + + jit_compiler_set_llvm_jit_func_ptr( + module, + i + j * group_stride + module->import_function_count, + (void *)func_addr); + + /* Try to switch to call this llvm jit funtion instead of + fast jit function from fast jit jitted code */ + jit_compiler_set_call_to_llvm_jit( + module, + i + j * group_stride + module->import_function_count); +#endif + } + } + + if (module->orcjit_stop_compiling) { + break; + } + } +#endif + + return NULL; +} + +static void +orcjit_stop_compile_threads(WASMModule *module) +{ + uint32 i, thread_num = (uint32)(sizeof(module->orcjit_thread_args) + / sizeof(OrcJitThreadArg)); + + module->orcjit_stop_compiling = true; + for (i = 0; i < thread_num; i++) { + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } +} + +static bool +compile_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + uint32 thread_num = + (uint32)(sizeof(module->orcjit_thread_args) / sizeof(OrcJitThreadArg)); + uint32 i, j; + bh_print_time("Begin to compile jit functions"); - thread_num = - (uint32)(sizeof(module->orcjit_thread_args) / sizeof(OrcJitThreadArg)); - /* Create threads to compile the jit functions */ - for (i = 0; i < thread_num; i++) { + for (i = 0; i < thread_num && i < module->function_count; i++) { +#if WASM_ENABLE_JIT != 0 module->orcjit_thread_args[i].comp_ctx = module->comp_ctx; +#endif module->orcjit_thread_args[i].module = module; module->orcjit_thread_args[i].group_idx = i; @@ -3102,8 +3305,6 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, (void *)&module->orcjit_thread_args[i], APP_THREAD_STACK_SIZE_DEFAULT) != 0) { - uint32 j; - set_error_buf(error_buf, error_buf_size, "create orcjit compile thread failed"); /* Terminate the threads created */ @@ -3118,15 +3319,39 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, #if WASM_ENABLE_LAZY_JIT == 0 /* Wait until all jit functions are compiled for eager mode */ for (i = 0; i < thread_num; i++) { - os_thread_join(module->orcjit_threads[i], NULL); + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } + +#if WASM_ENABLE_FAST_JIT != 0 + /* Ensure all the fast-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!jit_compiler_is_compiled(module, + i + module->import_function_count)) { + set_error_buf(error_buf, error_buf_size, + "failed to compile fast jit function"); + return false; + } } #endif +#if WASM_ENABLE_JIT != 0 + /* Ensure all the llvm-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!module->func_ptrs_compiled[i]) { + set_error_buf(error_buf, error_buf_size, + "failed to compile llvm jit function"); + return false; + } + } +#endif +#endif /* end of WASM_ENABLE_LAZY_JIT == 0 */ + bh_print_time("End compile jit functions"); return true; } -#endif /* end of WASM_ENABLE_JIT != 0 */ +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 */ static bool wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, @@ -3538,23 +3763,41 @@ load_from_sections(WASMModule *module, WASMSection *sections, calculate_global_data_offset(module); #if WASM_ENABLE_FAST_JIT != 0 - if (module->function_count - && !(module->fast_jit_func_ptrs = - loader_malloc(sizeof(void *) * module->function_count, - error_buf, error_buf_size))) { - return false; - } - if (!jit_compiler_compile_all(module)) { - set_error_buf(error_buf, error_buf_size, "fast jit compilation failed"); + if (!init_fast_jit_functions(module, error_buf, error_buf_size)) { return false; } #endif #if WASM_ENABLE_JIT != 0 - if (!compile_llvm_jit_functions(module, error_buf, error_buf_size)) { + if (!init_llvm_jit_functions_stage1(module, error_buf, error_buf_size)) { return false; } -#endif /* end of WASM_ENABLE_JIT != 0 */ +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + return false; + } +#else + /* Run aot_compile_wasm in a backend thread, so as not to block the main + thread fast jit execution, since applying llvm optimizations in + aot_compile_wasm may cost a lot of time. + Create thread with enough native stack to apply llvm optimizations */ + if (os_thread_create(&module->llvm_jit_init_thread, + init_llvm_jit_functions_stage2_callback, + (void *)module, APP_THREAD_STACK_SIZE_DEFAULT * 8) + != 0) { + set_error_buf(error_buf, error_buf_size, + "create orcjit compile thread failed"); + return false; + } +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Create threads to compile the jit functions */ + if (!compile_jit_functions(module, error_buf, error_buf_size)) { + return false; + } +#endif #if WASM_ENABLE_MEMORY_TRACING != 0 wasm_runtime_dump_module_mem_consumption((WASMModuleCommon *)module); @@ -3567,9 +3810,7 @@ create_module(char *error_buf, uint32 error_buf_size) { WASMModule *module = loader_malloc(sizeof(WASMModule), error_buf, error_buf_size); -#if WASM_ENABLE_FAST_INTERP == 0 bh_list_status ret; -#endif if (!module) { return NULL; @@ -3584,19 +3825,31 @@ create_module(char *error_buf, uint32 error_buf_size) module->br_table_cache_list = &module->br_table_cache_list_head; ret = bh_list_init(module->br_table_cache_list); bh_assert(ret == BH_LIST_SUCCESS); - (void)ret; #endif #if WASM_ENABLE_MULTI_MODULE != 0 module->import_module_list = &module->import_module_list_head; + ret = bh_list_init(module->import_module_list); + bh_assert(ret == BH_LIST_SUCCESS); #endif + #if WASM_ENABLE_DEBUG_INTERP != 0 - bh_list_init(&module->fast_opcode_list); - if (os_mutex_init(&module->ref_count_lock) != 0) { + ret = bh_list_init(&module->fast_opcode_list); + bh_assert(ret == BH_LIST_SUCCESS); +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ + && WASM_ENABLE_LAZY_JIT != 0) + if (os_mutex_init(&module->instance_list_lock) != 0) { + set_error_buf(error_buf, error_buf_size, + "init instance list lock failed"); wasm_runtime_free(module); return NULL; } #endif + + (void)ret; return module; } @@ -3964,10 +4217,19 @@ wasm_loader_unload(WASMModule *module) if (!module) return; -#if WASM_ENABLE_JIT != 0 - /* Stop LLVM JIT compilation firstly to avoid accessing +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 + module->orcjit_stop_compiling = true; + if (module->llvm_jit_init_thread) + os_thread_join(module->llvm_jit_init_thread, NULL); +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Stop Fast/LLVM JIT compilation firstly to avoid accessing module internal data after they were freed */ orcjit_stop_compile_threads(module); +#endif + +#if WASM_ENABLE_JIT != 0 if (module->func_ptrs) wasm_runtime_free(module->func_ptrs); if (module->comp_ctx) @@ -3976,6 +4238,13 @@ wasm_loader_unload(WASMModule *module) aot_destroy_comp_data(module->comp_data); #endif +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 + if (module->tierup_wait_lock_inited) { + os_mutex_destroy(&module->tierup_wait_lock); + os_cond_destroy(&module->tierup_wait_cond); + } +#endif + if (module->types) { for (i = 0; i < module->type_count; i++) { if (module->types[i]) @@ -3997,6 +4266,18 @@ wasm_loader_unload(WASMModule *module) wasm_runtime_free(module->functions[i]->code_compiled); if (module->functions[i]->consts) wasm_runtime_free(module->functions[i]->consts); +#endif +#if WASM_ENABLE_FAST_JIT != 0 + if (module->functions[i]->fast_jit_jitted_code) { + jit_code_cache_free( + module->functions[i]->fast_jit_jitted_code); + } +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->functions[i]->llvm_jit_func_ptr) { + jit_code_cache_free( + module->functions[i]->llvm_jit_func_ptr); + } +#endif #endif wasm_runtime_free(module->functions[i]); } @@ -4084,7 +4365,12 @@ wasm_loader_unload(WASMModule *module) wasm_runtime_free(fast_opcode); fast_opcode = next; } - os_mutex_destroy(&module->ref_count_lock); +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ + && WASM_ENABLE_LAZY_JIT != 0) + os_mutex_destroy(&module->instance_list_lock); #endif #if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 @@ -4093,12 +4379,14 @@ wasm_loader_unload(WASMModule *module) #if WASM_ENABLE_FAST_JIT != 0 if (module->fast_jit_func_ptrs) { - for (i = 0; i < module->function_count; i++) { - if (module->fast_jit_func_ptrs[i]) - jit_code_cache_free(module->fast_jit_func_ptrs[i]); - } wasm_runtime_free(module->fast_jit_func_ptrs); } + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (module->fast_jit_thread_locks_inited[i]) { + os_mutex_destroy(&module->fast_jit_thread_locks[i]); + } + } #endif wasm_runtime_free(module); diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index af116a823..cebd47eea 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -261,6 +261,12 @@ destroy_wasm_type(WASMType *type) return; } +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + if (type->call_to_llvm_jit_from_fast_jit) + jit_code_cache_free(type->call_to_llvm_jit_from_fast_jit); +#endif + wasm_runtime_free(type); } @@ -1783,93 +1789,78 @@ calculate_global_data_offset(WASMModule *module) module->global_data_size = data_offset; } -#if WASM_ENABLE_JIT != 0 -static void * -orcjit_thread_callback(void *arg) -{ - LLVMOrcJITTargetAddress func_addr = 0; - OrcJitThreadArg *thread_arg = (OrcJitThreadArg *)arg; - AOTCompContext *comp_ctx = thread_arg->comp_ctx; - WASMModule *module = thread_arg->module; - uint32 group_idx = thread_arg->group_idx; - uint32 group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; - uint32 func_count = module->function_count; - uint32 i, j; - typedef void (*F)(void); - LLVMErrorRef error; - char func_name[48]; - union { - F f; - void *v; - } u; - - /* Compile jit functions of this group */ - for (i = group_idx; i < func_count; - i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { - snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, i, - "_wrapper"); - LOG_DEBUG("compile func %s", func_name); - error = - LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, func_name); - if (error != LLVMErrorSuccess) { - char *err_msg = LLVMGetErrorMessage(error); - os_printf("failed to compile orc jit function: %s", err_msg); - LLVMDisposeErrorMessage(err_msg); - continue; - } - - /* Call the jit wrapper function to trigger its compilation, so as - to compile the actual jit functions, since we add the latter to - function list in the PartitionFunction callback */ - u.v = (void *)func_addr; - u.f(); - - for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { - if (i + j * group_stride < func_count) - module->func_ptrs_compiled[i + j * group_stride] = true; - } - - if (module->orcjit_stop_compiling) { - break; - } - } - - return NULL; -} - -static void -orcjit_stop_compile_threads(WASMModule *module) -{ - uint32 i, thread_num = (uint32)(sizeof(module->orcjit_thread_args) - / sizeof(OrcJitThreadArg)); - - module->orcjit_stop_compiling = true; - for (i = 0; i < thread_num; i++) { - if (module->orcjit_threads[i]) - os_thread_join(module->orcjit_threads[i], NULL); - } -} - +#if WASM_ENABLE_FAST_JIT != 0 static bool -compile_llvm_jit_functions(WASMModule *module, char *error_buf, - uint32 error_buf_size) +init_fast_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_LAZY_JIT != 0 + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); +#endif + uint32 i; + + if (!module->function_count) + return true; + + if (!(module->fast_jit_func_ptrs = + loader_malloc(sizeof(void *) * module->function_count, error_buf, + error_buf_size))) { + return false; + } + +#if WASM_ENABLE_LAZY_JIT != 0 + for (i = 0; i < module->function_count; i++) { + module->fast_jit_func_ptrs[i] = + jit_globals->compile_fast_jit_and_then_call; + } +#endif + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (os_mutex_init(&module->fast_jit_thread_locks[i]) != 0) { + set_error_buf(error_buf, error_buf_size, + "init fast jit thread lock failed"); + return false; + } + module->fast_jit_thread_locks_inited[i] = true; + } + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ + +#if WASM_ENABLE_JIT != 0 +static bool +init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf, + uint32 error_buf_size) { AOTCompOption option = { 0 }; char *aot_last_error; uint64 size; - uint32 thread_num, i; - if (module->function_count > 0) { - size = sizeof(void *) * (uint64)module->function_count - + sizeof(bool) * (uint64)module->function_count; - if (!(module->func_ptrs = - loader_malloc(size, error_buf, error_buf_size))) { - return false; - } - module->func_ptrs_compiled = - (bool *)((uint8 *)module->func_ptrs - + sizeof(void *) * module->function_count); + if (module->function_count == 0) + return true; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LLVM_JIT != 0 + if (os_mutex_init(&module->tierup_wait_lock) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup lock failed"); + return false; } + if (os_cond_init(&module->tierup_wait_cond) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup cond failed"); + os_mutex_destroy(&module->tierup_wait_lock); + return false; + } + module->tierup_wait_lock_inited = true; +#endif + + size = sizeof(void *) * (uint64)module->function_count + + sizeof(bool) * (uint64)module->function_count; + if (!(module->func_ptrs = loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + module->func_ptrs_compiled = + (bool *)((uint8 *)module->func_ptrs + + sizeof(void *) * module->function_count); module->comp_data = aot_create_comp_data(module); if (!module->comp_data) { @@ -1910,6 +1901,19 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, return false; } + return true; +} + +static bool +init_llvm_jit_functions_stage2(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + char *aot_last_error; + uint32 i; + + if (module->function_count == 0) + return true; + if (!aot_compile_wasm(module->comp_ctx)) { aot_last_error = aot_get_last_error(); bh_assert(aot_last_error != NULL); @@ -1917,7 +1921,12 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, return false; } - bh_print_time("Begin to lookup jit functions"); +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->orcjit_stop_compiling) + return false; +#endif + + bh_print_time("Begin to lookup llvm jit functions"); for (i = 0; i < module->function_count; i++) { LLVMOrcJITTargetAddress func_addr = 0; @@ -1929,9 +1938,9 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, func_name); if (error != LLVMErrorSuccess) { char *err_msg = LLVMGetErrorMessage(error); - char buf[128]; + char buf[96]; snprintf(buf, sizeof(buf), - "failed to compile orc jit function: %s\n", err_msg); + "failed to compile llvm jit function: %s", err_msg); set_error_buf(error_buf, error_buf_size, buf); LLVMDisposeErrorMessage(err_msg); return false; @@ -1944,17 +1953,211 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, * loading/storing at the same time. */ module->func_ptrs[i] = (void *)func_addr; - module->functions[i]->llvm_jit_func_ptr = (void *)func_addr; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->orcjit_stop_compiling) + return false; +#endif } + bh_print_time("End lookup llvm jit functions"); + + return true; +} +#endif /* end of WASM_ENABLE_JIT != 0 */ + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 +static void * +init_llvm_jit_functions_stage2_callback(void *arg) +{ + WASMModule *module = (WASMModule *)arg; + char error_buf[128]; + uint32 error_buf_size = (uint32)sizeof(error_buf); + + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + module->orcjit_stop_compiling = true; + return NULL; + } + + os_mutex_lock(&module->tierup_wait_lock); + module->llvm_jit_inited = true; + os_cond_broadcast(&module->tierup_wait_cond); + os_mutex_unlock(&module->tierup_wait_lock); + + return NULL; +} +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 +/* The callback function to compile jit functions */ +static void * +orcjit_thread_callback(void *arg) +{ + OrcJitThreadArg *thread_arg = (OrcJitThreadArg *)arg; +#if WASM_ENABLE_JIT != 0 + AOTCompContext *comp_ctx = thread_arg->comp_ctx; +#endif + WASMModule *module = thread_arg->module; + uint32 group_idx = thread_arg->group_idx; + uint32 group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; + uint32 func_count = module->function_count; + uint32 i; + +#if WASM_ENABLE_FAST_JIT != 0 + /* Compile fast jit funcitons of this group */ + for (i = group_idx; i < func_count; i += group_stride) { + if (!jit_compiler_compile(module, i + module->import_function_count)) { + os_printf("failed to compile fast jit function %u\n", i); + break; + } + + if (module->orcjit_stop_compiling) { + return NULL; + } + } +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* For JIT tier-up, set each llvm jit func to call_to_fast_jit */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + uint32 j; + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + if (!jit_compiler_set_call_to_fast_jit( + module, + i + j * group_stride + module->import_function_count)) { + os_printf( + "failed to compile call_to_fast_jit for func %u\n", + i + j * group_stride + module->import_function_count); + module->orcjit_stop_compiling = true; + return NULL; + } + } + if (module->orcjit_stop_compiling) { + return NULL; + } + } + } + + /* Wait until init_llvm_jit_functions_stage2 finishes */ + os_mutex_lock(&module->tierup_wait_lock); + while (!module->llvm_jit_inited) { + os_cond_reltimedwait(&module->tierup_wait_cond, + &module->tierup_wait_lock, 10); + if (module->orcjit_stop_compiling) { + /* init_llvm_jit_functions_stage2 failed */ + os_mutex_unlock(&module->tierup_wait_lock); + return NULL; + } + } + os_mutex_unlock(&module->tierup_wait_lock); +#endif + +#if WASM_ENABLE_JIT != 0 + /* Compile llvm jit functions of this group */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + LLVMOrcJITTargetAddress func_addr = 0; + LLVMErrorRef error; + char func_name[48]; + typedef void (*F)(void); + union { + F f; + void *v; + } u; + uint32 j; + + snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, i, + "_wrapper"); + LOG_DEBUG("compile llvm jit func %s", func_name); + error = + LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + os_printf("failed to compile llvm jit function %u: %s", i, err_msg); + LLVMDisposeErrorMessage(err_msg); + break; + } + + /* Call the jit wrapper function to trigger its compilation, so as + to compile the actual jit functions, since we add the latter to + function list in the PartitionFunction callback */ + u.v = (void *)func_addr; + u.f(); + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + module->func_ptrs_compiled[i + j * group_stride] = true; +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, + i + j * group_stride); + error = LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, + func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + os_printf("failed to compile llvm jit function %u: %s", i, + err_msg); + LLVMDisposeErrorMessage(err_msg); + /* Ignore current llvm jit func, as its func ptr is + previous set to call_to_fast_jit, which also works */ + continue; + } + + jit_compiler_set_llvm_jit_func_ptr( + module, + i + j * group_stride + module->import_function_count, + (void *)func_addr); + + /* Try to switch to call this llvm jit funtion instead of + fast jit function from fast jit jitted code */ + jit_compiler_set_call_to_llvm_jit( + module, + i + j * group_stride + module->import_function_count); +#endif + } + } + + if (module->orcjit_stop_compiling) { + break; + } + } +#endif + + return NULL; +} + +static void +orcjit_stop_compile_threads(WASMModule *module) +{ + uint32 i, thread_num = (uint32)(sizeof(module->orcjit_thread_args) + / sizeof(OrcJitThreadArg)); + + module->orcjit_stop_compiling = true; + for (i = 0; i < thread_num; i++) { + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } +} + +static bool +compile_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + uint32 thread_num = + (uint32)(sizeof(module->orcjit_thread_args) / sizeof(OrcJitThreadArg)); + uint32 i, j; + bh_print_time("Begin to compile jit functions"); - thread_num = - (uint32)(sizeof(module->orcjit_thread_args) / sizeof(OrcJitThreadArg)); - /* Create threads to compile the jit functions */ - for (i = 0; i < thread_num; i++) { + for (i = 0; i < thread_num && i < module->function_count; i++) { +#if WASM_ENABLE_JIT != 0 module->orcjit_thread_args[i].comp_ctx = module->comp_ctx; +#endif module->orcjit_thread_args[i].module = module; module->orcjit_thread_args[i].group_idx = i; @@ -1962,8 +2165,6 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, (void *)&module->orcjit_thread_args[i], APP_THREAD_STACK_SIZE_DEFAULT) != 0) { - uint32 j; - set_error_buf(error_buf, error_buf_size, "create orcjit compile thread failed"); /* Terminate the threads created */ @@ -1978,15 +2179,39 @@ compile_llvm_jit_functions(WASMModule *module, char *error_buf, #if WASM_ENABLE_LAZY_JIT == 0 /* Wait until all jit functions are compiled for eager mode */ for (i = 0; i < thread_num; i++) { - os_thread_join(module->orcjit_threads[i], NULL); + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } + +#if WASM_ENABLE_FAST_JIT != 0 + /* Ensure all the fast-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!jit_compiler_is_compiled(module, + i + module->import_function_count)) { + set_error_buf(error_buf, error_buf_size, + "failed to compile fast jit function"); + return false; + } } #endif +#if WASM_ENABLE_JIT != 0 + /* Ensure all the llvm-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!module->func_ptrs_compiled[i]) { + set_error_buf(error_buf, error_buf_size, + "failed to compile llvm jit function"); + return false; + } + } +#endif +#endif /* end of WASM_ENABLE_LAZY_JIT == 0 */ + bh_print_time("End compile jit functions"); return true; } -#endif /* end of WASM_ENABLE_JIT != 0 */ +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 */ #if WASM_ENABLE_REF_TYPES != 0 static bool @@ -2427,22 +2652,41 @@ load_from_sections(WASMModule *module, WASMSection *sections, calculate_global_data_offset(module); #if WASM_ENABLE_FAST_JIT != 0 - if (!(module->fast_jit_func_ptrs = - loader_malloc(sizeof(void *) * module->function_count, error_buf, - error_buf_size))) { - return false; - } - if (!jit_compiler_compile_all(module)) { - set_error_buf(error_buf, error_buf_size, "fast jit compilation failed"); + if (!init_fast_jit_functions(module, error_buf, error_buf_size)) { return false; } #endif #if WASM_ENABLE_JIT != 0 - if (!compile_llvm_jit_functions(module, error_buf, error_buf_size)) { + if (!init_llvm_jit_functions_stage1(module, error_buf, error_buf_size)) { return false; } -#endif /* end of WASM_ENABLE_JIT != 0 */ +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + return false; + } +#else + /* Run aot_compile_wasm in a backend thread, so as not to block the main + thread fast jit execution, since applying llvm optimizations in + aot_compile_wasm may cost a lot of time. + Create thread with enough native stack to apply llvm optimizations */ + if (os_thread_create(&module->llvm_jit_init_thread, + init_llvm_jit_functions_stage2_callback, + (void *)module, APP_THREAD_STACK_SIZE_DEFAULT * 8) + != 0) { + set_error_buf(error_buf, error_buf_size, + "create orcjit compile thread failed"); + return false; + } +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Create threads to compile the jit functions */ + if (!compile_jit_functions(module, error_buf, error_buf_size)) { + return false; + } +#endif #if WASM_ENABLE_MEMORY_TRACING != 0 wasm_runtime_dump_module_mem_consumption(module); @@ -2455,9 +2699,7 @@ create_module(char *error_buf, uint32 error_buf_size) { WASMModule *module = loader_malloc(sizeof(WASMModule), error_buf, error_buf_size); -#if WASM_ENABLE_FAST_INTERP == 0 bh_list_status ret; -#endif if (!module) { return NULL; @@ -2472,9 +2714,18 @@ create_module(char *error_buf, uint32 error_buf_size) module->br_table_cache_list = &module->br_table_cache_list_head; ret = bh_list_init(module->br_table_cache_list); bh_assert(ret == BH_LIST_SUCCESS); - (void)ret; #endif +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 + if (os_mutex_init(&module->instance_list_lock) != 0) { + set_error_buf(error_buf, error_buf_size, + "init instance list lock failed"); + wasm_runtime_free(module); + return NULL; + } +#endif + + (void)ret; return module; } @@ -2686,10 +2937,19 @@ wasm_loader_unload(WASMModule *module) if (!module) return; -#if WASM_ENABLE_JIT != 0 - /* Stop LLVM JIT compilation firstly to avoid accessing +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 + module->orcjit_stop_compiling = true; + if (module->llvm_jit_init_thread) + os_thread_join(module->llvm_jit_init_thread, NULL); +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Stop Fast/LLVM JIT compilation firstly to avoid accessing module internal data after they were freed */ orcjit_stop_compile_threads(module); +#endif + +#if WASM_ENABLE_JIT != 0 if (module->func_ptrs) wasm_runtime_free(module->func_ptrs); if (module->comp_ctx) @@ -2698,6 +2958,13 @@ wasm_loader_unload(WASMModule *module) aot_destroy_comp_data(module->comp_data); #endif +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 + if (module->tierup_wait_lock_inited) { + os_mutex_destroy(&module->tierup_wait_lock); + os_cond_destroy(&module->tierup_wait_cond); + } +#endif + if (module->types) { for (i = 0; i < module->type_count; i++) { if (module->types[i]) @@ -2719,6 +2986,18 @@ wasm_loader_unload(WASMModule *module) wasm_runtime_free(module->functions[i]->code_compiled); if (module->functions[i]->consts) wasm_runtime_free(module->functions[i]->consts); +#endif +#if WASM_ENABLE_FAST_JIT != 0 + if (module->functions[i]->fast_jit_jitted_code) { + jit_code_cache_free( + module->functions[i]->fast_jit_jitted_code); + } +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->functions[i]->llvm_jit_func_ptr) { + jit_code_cache_free( + module->functions[i]->llvm_jit_func_ptr); + } +#endif #endif wasm_runtime_free(module->functions[i]); } @@ -2775,14 +3054,20 @@ wasm_loader_unload(WASMModule *module) } #endif +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT && WASM_ENABLE_LAZY_JIT != 0 + os_mutex_destroy(&module->instance_list_lock); +#endif + #if WASM_ENABLE_FAST_JIT != 0 if (module->fast_jit_func_ptrs) { - for (i = 0; i < module->function_count; i++) { - if (module->fast_jit_func_ptrs[i]) - jit_code_cache_free(module->fast_jit_func_ptrs[i]); - } wasm_runtime_free(module->fast_jit_func_ptrs); } + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (module->fast_jit_thread_locks_inited[i]) { + os_mutex_destroy(&module->fast_jit_thread_locks[i]); + } + } #endif wasm_runtime_free(module); @@ -5572,12 +5857,6 @@ re_scan: if (depth > 255) { /* The depth cannot be stored in one byte, create br_table cache to store each depth */ -#if WASM_ENABLE_DEBUG_INTERP != 0 - if (!record_fast_op(module, p_org, *p_org, - error_buf, error_buf_size)) { - goto fail; - } -#endif if (!(br_table_cache = loader_malloc( offsetof(BrTableCache, br_depths) + sizeof(uint32) diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 1da8a19ad..311e54abe 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -19,6 +19,9 @@ #if WASM_ENABLE_DEBUG_INTERP != 0 #include "../libraries/debug-engine/debug_engine.h" #endif +#if WASM_ENABLE_FAST_JIT != 0 +#include "../fast-jit/jit_compiler.h" +#endif #if WASM_ENABLE_JIT != 0 #include "../aot/aot_runtime.h" #endif @@ -1414,30 +1417,10 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, extra_info_offset = (uint32)total_size; total_size += sizeof(WASMModuleInstanceExtra); -#if WASM_ENABLE_DEBUG_INTERP != 0 - if (!is_sub_inst) { - os_mutex_lock(&module->ref_count_lock); - if (module->ref_count != 0) { - LOG_WARNING( - "warning: multiple instances referencing the same module may " - "cause unexpected behaviour during debugging"); - } - module->ref_count++; - os_mutex_unlock(&module->ref_count_lock); - } -#endif - /* Allocate the memory for module instance with memory instances, global data, table data appended at the end */ if (!(module_inst = runtime_malloc(total_size, error_buf, error_buf_size))) { -#if WASM_ENABLE_DEBUG_INTERP != 0 - if (!is_sub_inst) { - os_mutex_lock(&module->ref_count_lock); - module->ref_count--; - os_mutex_unlock(&module->ref_count_lock); - } -#endif return NULL; } @@ -1827,6 +1810,33 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, } #endif +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0) + if (!is_sub_inst) { + /* Add module instance into module's instance list */ + os_mutex_lock(&module->instance_list_lock); +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (module->instance_list) { + LOG_WARNING( + "warning: multiple instances referencing to the same module " + "may cause unexpected behaviour during debugging"); + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* Copy llvm func ptrs again in case that they were updated + after the module instance was created */ + bh_memcpy_s(module_inst->func_ptrs + module->import_function_count, + sizeof(void *) * module->function_count, module->func_ptrs, + sizeof(void *) * module->function_count); +#endif + module_inst->e->next = module->instance_list; + module->instance_list = module_inst; + os_mutex_unlock(&module->instance_list_lock); + } +#endif + if (module->start_function != (uint32)-1) { /* TODO: fix start function can be import function issue */ if (module->start_function >= module->import_function_count) @@ -1864,8 +1874,8 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size, wasm_runtime_dump_module_inst_mem_consumption( (WASMModuleInstanceCommon *)module_inst); #endif - (void)global_data_end; + (void)global_data_end; return module_inst; fail: @@ -1935,11 +1945,28 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) } #endif -#if WASM_ENABLE_DEBUG_INTERP != 0 +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0) if (!is_sub_inst) { - os_mutex_lock(&module_inst->module->ref_count_lock); - module_inst->module->ref_count--; - os_mutex_unlock(&module_inst->module->ref_count_lock); + WASMModule *module = module_inst->module; + WASMModuleInstance *instance_prev = NULL, *instance; + os_mutex_lock(&module->instance_list_lock); + + instance = module->instance_list; + while (instance) { + if (instance == module_inst) { + if (!instance_prev) + module->instance_list = instance->e->next; + else + instance_prev->e->next = instance->e->next; + break; + } + instance_prev = instance; + instance = instance->e->next; + } + + os_mutex_unlock(&module->instance_list_lock); } #endif @@ -2819,7 +2846,7 @@ fast_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, return call_indirect(exec_env, tbl_idx, elem_idx, argc, argv, true, type_idx); } -#endif +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ #if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 1ebac5f3c..6d1a5340d 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -59,6 +59,9 @@ typedef enum WASMExceptionID { EXCE_AUX_STACK_UNDERFLOW, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, EXCE_OPERAND_STACK_OVERFLOW, +#if WASM_ENABLE_FAST_JIT != 0 + EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC, +#endif EXCE_ALREADY_THROWN, EXCE_NUM, } WASMExceptionID; @@ -233,6 +236,12 @@ typedef struct WASMModuleInstanceExtra { #if WASM_ENABLE_MEMORY_PROFILING != 0 uint32 max_aux_stack_used; #endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ + && WASM_ENABLE_LAZY_JIT != 0) + WASMModuleInstance *next; +#endif } WASMModuleInstanceExtra; struct AOTFuncPerfProfInfo; diff --git a/doc/build_wamr.md b/doc/build_wamr.md index d3c7a8265..f32d69bca 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -240,7 +240,7 @@ make By default in Linux, the `fast interpreter`, `AOT` and `Libc WASI` are enabled, and JIT is disabled. And the build target is set to X86_64 or X86_32 depending on the platform's bitwidth. -There are total 5 running modes supported: fast interpreter, classi interpreter, AOT, LLVM JIT and Fast JIT. +There are total 6 running modes supported: fast interpreter, classi interpreter, AOT, LLVM JIT, Fast JIT and Multi-tier JIT. (1) To run a wasm file with `fast interpreter` mode - build iwasm with default build and then: ```Bash @@ -301,6 +301,14 @@ make ``` The Fast JIT is a lightweight JIT engine with quick startup, small footprint and good portability, and gains ~50% performance of AOT. +(6) To enable the `Multi-tier JIT` mode: +``` Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_FAST_JTI=1 -DWAMR_BUILD_JIT=1 +make +``` +The Multi-tier JIT is a two level JIT tier-up engine, which launchs Fast JIT to run the wasm module as soon as possible and creates backend threads to compile the LLVM JIT functions at the same time, and when the LLVM JIT functions are compiled, the runtime will switch the extecution from the Fast JIT jitted code to LLVM JIT jitted code gradually, so as to gain the best performance. + Linux SGX (Intel Software Guard Extension) ------------------------- diff --git a/tests/wamr-test-suites/test_wamr.sh b/tests/wamr-test-suites/test_wamr.sh index b6ab29784..dca7f87c8 100755 --- a/tests/wamr-test-suites/test_wamr.sh +++ b/tests/wamr-test-suites/test_wamr.sh @@ -16,7 +16,7 @@ function help() echo "-c clean previous test results, not start test" echo "-s {suite_name} test only one suite (spec)" echo "-m set compile target of iwasm(x86_64\x86_32\armv7_vfp\thumbv7_vfp\riscv64_lp64d\riscv64_lp64)" - echo "-t set compile type of iwasm(classic-interp\fast-interp\jit\aot\fast-jit)" + echo "-t set compile type of iwasm(classic-interp\fast-interp\jit\aot\fast-jit\multi-tier-jit)" echo "-M enable multi module feature" echo "-p enable multi thread feature" echo "-S enable SIMD feature" @@ -31,7 +31,7 @@ function help() OPT_PARSED="" WABT_BINARY_RELEASE="NO" #default type -TYPE=("classic-interp" "fast-interp" "jit" "aot" "fast-jit") +TYPE=("classic-interp" "fast-interp" "jit" "aot" "fast-jit" "multi-tier-jit") #default target TARGET="X86_64" ENABLE_MULTI_MODULE=0 @@ -84,7 +84,8 @@ do t) echo "set compile type of wamr " ${OPTARG} if [[ ${OPTARG} != "classic-interp" && ${OPTARG} != "fast-interp" \ - && ${OPTARG} != "jit" && ${OPTARG} != "aot" && ${OPTARG} != "fast-jit" ]]; then + && ${OPTARG} != "jit" && ${OPTARG} != "aot" + && ${OPTARG} != "fast-jit" && ${OPTARG} != "multi-tier-jit" ]]; then echo "*----- please varify a type of compile when using -t! -----*" help exit 1 @@ -213,6 +214,12 @@ readonly FAST_JIT_COMPILE_FLAGS="\ -DWAMR_BUILD_FAST_JIT=1 \ -DWAMR_BUILD_SPEC_TEST=1" +readonly MULTI_TIER_JIT_COMPILE_FLAGS="\ + -DWAMR_BUILD_TARGET=${TARGET} \ + -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=0 \ + -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=1 \ + -DWAMR_BUILD_SPEC_TEST=1" + readonly COMPILE_FLAGS=( "${CLASSIC_INTERP_COMPILE_FLAGS}" "${FAST_INTERP_COMPILE_FLAGS}" @@ -220,6 +227,7 @@ readonly COMPILE_FLAGS=( "${ORC_LAZY_JIT_COMPILE_FLAGS}" "${AOT_COMPILE_FLAGS}" "${FAST_JIT_COMPILE_FLAGS}" + "${MULTI_TIER_JIT_COMPILE_FLAGS}" ) # TODO: with libiwasm.so only @@ -409,6 +417,10 @@ function spec_test() echo "fast-jit doesn't support multi-thread feature yet, skip it" return fi + if [[ $1 == 'multi-tier-jit' ]]; then + echo "multi-tier-jit doesn't support multi-thread feature yet, skip it" + return + fi fi if [[ ${ENABLE_XIP} == 1 ]]; then @@ -667,7 +679,7 @@ function trigger() "fast-jit") echo "work in fast-jit mode" - # jit + # fast-jit BUILD_FLAGS="$FAST_JIT_COMPILE_FLAGS $EXTRA_COMPILE_FLAGS" build_iwasm_with_cfg $BUILD_FLAGS for suite in "${TEST_CASE_ARR[@]}"; do @@ -675,6 +687,16 @@ function trigger() done ;; + "multi-tier-jit") + echo "work in multi-tier-jit mode" + # multi-tier-jit + BUILD_FLAGS="$MULTI_TIER_JIT_COMPILE_FLAGS $EXTRA_COMPILE_FLAGS" + build_iwasm_with_cfg $BUILD_FLAGS + for suite in "${TEST_CASE_ARR[@]}"; do + $suite"_test" multi-tier-jit + done + ;; + *) echo "unexpected mode, do nothing" ;; From 7d19b229eb56bb0cd4bf34a7a07fa031f007c76e Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Thu, 22 Dec 2022 10:02:23 +0000 Subject: [PATCH 22/33] feat(wasi-threads): Add CI for WASI threads (#1819) Update wasi-sdk from 12.0 to 16.0 in CI Build wasi-libc and build wai-threads sample in CI --- .../compilation_on_android_ubuntu.yml | 27 ++++++++++++++++--- .github/workflows/compilation_on_macos.yml | 27 ++++++++++++++++--- .github/workflows/compilation_on_sgx.yml | 25 ++++++++++++++--- samples/wasi-threads/wasm-apps/CMakeLists.txt | 2 ++ 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index 06a921001..fd9831676 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -232,7 +232,7 @@ jobs: os: [ubuntu-20.04, ubuntu-22.04] wasi_sdk_release: [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk-16.0-linux.tar.gz", ] wabt_release: [ @@ -299,7 +299,7 @@ jobs: os: [ubuntu-20.04, ubuntu-22.04] wasi_sdk_release: [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk-16.0-linux.tar.gz", ] wabt_release: [ @@ -313,8 +313,8 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wasi_sdk_release }} - sudo tar -xzf wasi-sdk-12.0-*.tar.gz - sudo mv wasi-sdk-12.0 wasi-sdk + sudo tar -xzf wasi-sdk-16.0-*.tar.gz + sudo mv wasi-sdk-16.0 wasi-sdk - name: download and install wabt run: | @@ -323,6 +323,17 @@ jobs: sudo tar -xzf wabt-1.0.24-*.tar.gz sudo mv wabt-1.0.24 wabt + - name: build wasi-libc (needed for wasi-threads) + run: | + git clone --branch wasi-sdk-17 https://github.com/WebAssembly/wasi-libc + cd wasi-libc + make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps + - name: Build Sample [basic] run: | cd samples/basic @@ -376,6 +387,14 @@ jobs: exit $? working-directory: ./samples/simple + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm + spec_test: needs: [build_iwasm, build_llvm_libraries, build_wamrc] runs-on: ubuntu-20.04 diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index 73ebec892..1e5ff415b 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -210,7 +210,7 @@ jobs: #$AOT_BUILD_OPTIONS, ] os: [macos-latest] - wasi_sdk_release: ["https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-macos.tar.gz"] + wasi_sdk_release: ["https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk-16.0-macos.tar.gz"] wabt_release: ["https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-macos.tar.gz"] steps: - name: checkout @@ -246,7 +246,7 @@ jobs: strategy: matrix: os: [macos-latest] - wasi_sdk_release: ["https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-macos.tar.gz"] + wasi_sdk_release: ["https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk-16.0-macos.tar.gz"] wabt_release: ["https://github.com/WebAssembly/wabt/releases/download/1.0.24/wabt-1.0.24-macos.tar.gz"] steps: - name: checkout @@ -256,8 +256,8 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wasi_sdk_release }} - sudo tar -xzf wasi-sdk-12.0-*.tar.gz - sudo mv wasi-sdk-12.0 wasi-sdk + sudo tar -xzf wasi-sdk-16.0-*.tar.gz + sudo mv wasi-sdk-16.0 wasi-sdk - name: download and install wabt run: | @@ -266,6 +266,17 @@ jobs: sudo tar -xzf wabt-1.0.24-*.tar.gz sudo mv wabt-1.0.24 wabt + - name: build wasi-libc (needed for wasi-threads) + run: | + git clone --branch wasi-sdk-17 https://github.com/WebAssembly/wasi-libc + cd wasi-libc + make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps + - name: Build Sample [basic] run: | cd samples/basic @@ -311,3 +322,11 @@ jobs: cmake .. cmake --build . --config Release --parallel 4 ./hello + + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm \ No newline at end of file diff --git a/.github/workflows/compilation_on_sgx.yml b/.github/workflows/compilation_on_sgx.yml index 70eeaec02..c4d4142dd 100644 --- a/.github/workflows/compilation_on_sgx.yml +++ b/.github/workflows/compilation_on_sgx.yml @@ -244,7 +244,7 @@ jobs: os: [ubuntu-20.04] wasi_sdk_release: [ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-12/wasi-sdk-12.0-linux.tar.gz", + "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk-16.0-linux.tar.gz", ] wabt_release: [ @@ -258,8 +258,8 @@ jobs: run: | cd /opt sudo wget ${{ matrix.wasi_sdk_release }} - sudo tar -xzf wasi-sdk-12.0-*.tar.gz - sudo mv wasi-sdk-12.0 wasi-sdk + sudo tar -xzf wasi-sdk-16.0-*.tar.gz + sudo mv wasi-sdk-16.0 wasi-sdk - name: download and install wabt run: | @@ -268,6 +268,17 @@ jobs: sudo tar -xzf wabt-1.0.24-*.tar.gz sudo mv wabt-1.0.24 wabt + - name: build wasi-libc (needed for wasi-threads) + run: | + git clone --branch wasi-sdk-17 https://github.com/WebAssembly/wasi-libc + cd wasi-libc + make \ + AR=/opt/wasi-sdk/bin/llvm-ar \ + NM=/opt/wasi-sdk/bin/llvm-nm \ + CC=/opt/wasi-sdk/bin/clang \ + THREAD_MODEL=posix + working-directory: core/deps + - name: install SGX SDK and necessary libraries run: | mkdir -p /opt/intel @@ -327,6 +338,14 @@ jobs: cmake --build . --config Release --parallel 4 ./hello + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm + spec_test_default: needs: [build_iwasm, build_llvm_libraries, build_wamrc] runs-on: ubuntu-20.04 diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt index 25dba40b8..59f48713b 100644 --- a/samples/wasi-threads/wasm-apps/CMakeLists.txt +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -29,6 +29,8 @@ function (compile_sample SOURCE_FILE) LINKER:--export=__data_end LINKER:--shared-memory,--max-memory=1966080 LINKER:--export=wasi_thread_start + LINKER:--export=malloc + LINKER:--export=free ) endfunction () From 4e5529f21fccc2a40f83ea717fd091b955a22ad6 Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Fri, 6 Jan 2023 02:28:25 +0000 Subject: [PATCH 23/33] Reserve TID values for WASI threads (#1862) According to the [WASI thread specification](https://github.com/WebAssembly/wasi-threads/pull/16), some thread identifiers are reserved and should not be used. In fact, only IDs between `1` and `0x1FFFFFFF` are valid. The thread ID allocator has been moved to a separate class to avoid polluting the `lib_wasi_threads_wrapper` logic. --- .../lib-wasi-threads/lib_wasi_threads.cmake | 3 +- .../lib_wasi_threads_wrapper.c | 64 ++------------- .../lib-wasi-threads/tid_allocator.c | 80 +++++++++++++++++++ .../lib-wasi-threads/tid_allocator.h | 36 +++++++++ 4 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 core/iwasm/libraries/lib-wasi-threads/tid_allocator.c create mode 100644 core/iwasm/libraries/lib-wasi-threads/tid_allocator.h diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake index 49f87077f..54d2ba902 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads.cmake @@ -8,4 +8,5 @@ add_definitions (-DWASM_ENABLE_LIB_WASI_THREADS=1 -DWASM_ENABLE_HEAP_AUX_STACK_A include_directories(${LIB_WASI_THREADS_DIR}) set (LIB_WASI_THREADS_SOURCE - ${LIB_WASI_THREADS_DIR}/lib_wasi_threads_wrapper.c) \ No newline at end of file + ${LIB_WASI_THREADS_DIR}/lib_wasi_threads_wrapper.c + ${LIB_WASI_THREADS_DIR}/tid_allocator.c) \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 2fa90cee3..846c908c9 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -5,6 +5,7 @@ #include "bh_log.h" #include "thread_manager.h" +#include "tid_allocator.h" #if WASM_ENABLE_INTERP != 0 #include "wasm_runtime.h" @@ -15,16 +16,8 @@ #endif static const char *THREAD_START_FUNCTION = "wasi_thread_start"; - static korp_mutex thread_id_lock; - -// Stack data structure to track available thread identifiers -#define AVAIL_TIDS_INIT_SIZE CLUSTER_MAX_THREAD_NUM -typedef struct { - int32 *ids; - uint32 pos, size; -} AvailableThreadIds; -static AvailableThreadIds avail_tids; +static TidAllocator tid_allocator; typedef struct { /* app's entry function */ @@ -38,41 +31,10 @@ typedef struct { static int32 allocate_thread_id() { - int32 id = -1; - os_mutex_lock(&thread_id_lock); - if (avail_tids.pos == 0) { // Resize stack and push new thread ids - uint32 old_size = avail_tids.size; - uint32 new_size = avail_tids.size * 2; - if (new_size / 2 != avail_tids.size) { - LOG_ERROR("Overflow detected during new size calculation"); - goto return_id; - } - - size_t realloc_size = new_size * sizeof(int32); - if (realloc_size / sizeof(int32) != new_size) { - LOG_ERROR("Overflow detected during realloc"); - goto return_id; - } - int32 *tmp = - (int32 *)wasm_runtime_realloc(avail_tids.ids, realloc_size); - if (tmp == NULL) { - LOG_ERROR("Thread ID allocator realloc failed"); - goto return_id; - } - - avail_tids.size = new_size; - avail_tids.pos = old_size; - avail_tids.ids = tmp; - for (uint32 i = 0; i < old_size; i++) - avail_tids.ids[i] = new_size - i; - } - - // Pop available thread identifier from `avail_tids` stack - id = avail_tids.ids[--avail_tids.pos]; - -return_id: + int32 id = tid_allocator_get_tid(&tid_allocator); os_mutex_unlock(&thread_id_lock); + return id; } @@ -80,11 +42,7 @@ void deallocate_thread_id(int32 thread_id) { os_mutex_lock(&thread_id_lock); - - // Release thread identifier by pushing it into `avail_tids` stack - bh_assert(avail_tids.pos < avail_tids.size); - avail_tids.ids[avail_tids.pos++] = thread_id; - + tid_allocator_release_tid(&tid_allocator, thread_id); os_mutex_unlock(&thread_id_lock); } @@ -212,17 +170,10 @@ lib_wasi_threads_init(void) if (0 != os_mutex_init(&thread_id_lock)) return false; - // Initialize stack to store thread identifiers - avail_tids.size = AVAIL_TIDS_INIT_SIZE; - avail_tids.pos = avail_tids.size; - avail_tids.ids = - (int32 *)wasm_runtime_malloc(avail_tids.size * sizeof(int32)); - if (avail_tids.ids == NULL) { + if (!tid_allocator_init(&tid_allocator)) { os_mutex_destroy(&thread_id_lock); return false; } - for (uint32 i = 0; i < avail_tids.size; i++) - avail_tids.ids[i] = avail_tids.size - i; return true; } @@ -230,7 +181,6 @@ lib_wasi_threads_init(void) void lib_wasi_threads_destroy(void) { - wasm_runtime_free(avail_tids.ids); - avail_tids.ids = NULL; + tid_allocator_deinit(&tid_allocator); os_mutex_destroy(&thread_id_lock); } diff --git a/core/iwasm/libraries/lib-wasi-threads/tid_allocator.c b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.c new file mode 100644 index 000000000..4d53da0c9 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "tid_allocator.h" +#include "wasm_export.h" +#include "bh_log.h" + +bh_static_assert(TID_MIN <= TID_MAX); +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +bool +tid_allocator_init(TidAllocator *tid_allocator) +{ + tid_allocator->size = MIN(TID_ALLOCATOR_INIT_SIZE, TID_MAX - TID_MIN + 1); + tid_allocator->pos = tid_allocator->size; + tid_allocator->ids = + wasm_runtime_malloc(tid_allocator->size * sizeof(int32)); + if (tid_allocator->ids == NULL) + return false; + + for (int64 i = tid_allocator->pos - 1; i >= 0; i--) + tid_allocator->ids[i] = TID_MIN + (tid_allocator->pos - 1 - i); + + return true; +} + +void +tid_allocator_deinit(TidAllocator *tid_allocator) +{ + wasm_runtime_free(tid_allocator->ids); +} + +int32 +tid_allocator_get_tid(TidAllocator *tid_allocator) +{ + if (tid_allocator->pos == 0) { // Resize stack and push new thread ids + if (tid_allocator->size == TID_MAX - TID_MIN + 1) { + LOG_ERROR("Maximum thread identifier reached"); + return -1; + } + + uint32 old_size = tid_allocator->size; + uint32 new_size = MIN(tid_allocator->size * 2, TID_MAX - TID_MIN + 1); + if (new_size != TID_MAX - TID_MIN + 1 + && new_size / 2 != tid_allocator->size) { + LOG_ERROR("Overflow detected during new size calculation"); + return -1; + } + + size_t realloc_size = new_size * sizeof(int32); + if (realloc_size / sizeof(int32) != new_size) { + LOG_ERROR("Overflow detected during realloc"); + return -1; + } + int32 *tmp = wasm_runtime_realloc(tid_allocator->ids, realloc_size); + if (tmp == NULL) { + LOG_ERROR("Thread ID allocator realloc failed"); + return -1; + } + + tid_allocator->size = new_size; + tid_allocator->pos = new_size - old_size; + tid_allocator->ids = tmp; + for (int64 i = tid_allocator->pos - 1; i >= 0; i--) + tid_allocator->ids[i] = TID_MIN + (tid_allocator->size - 1 - i); + } + + // Pop available thread identifier from the stack + return tid_allocator->ids[--tid_allocator->pos]; +} + +void +tid_allocator_release_tid(TidAllocator *tid_allocator, int32 thread_id) +{ + // Release thread identifier by pushing it into the stack + bh_assert(tid_allocator->pos < tid_allocator->size); + tid_allocator->ids[tid_allocator->pos++] = thread_id; +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h new file mode 100644 index 000000000..53af1719f --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _TID_ALLOCATOR_H +#define _TID_ALLOCATOR_H + +#include "platform_common.h" + +#define TID_ALLOCATOR_INIT_SIZE CLUSTER_MAX_THREAD_NUM +enum { + TID_MIN = 1, + TID_MAX = 0x1FFFFFFF +}; // Reserved TIDs (WASI specification) + +/* Stack data structure to track available thread identifiers */ +typedef struct { + int32 *ids; // Array used to store the stack + uint32 size; // Stack capacity + uint32 pos; // Index of the element after the stack top +} TidAllocator; + +bool +tid_allocator_init(TidAllocator *tid_allocator); + +void +tid_allocator_deinit(TidAllocator *tid_allocator); + +int32 +tid_allocator_get_tid(TidAllocator *tid_allocator); + +void +tid_allocator_release_tid(TidAllocator *tid_allocator, int32 thread_id); + +#endif /* _TID_ALLOCATOR_H */ \ No newline at end of file From 2615646c203278201e4eff5d8459d5167cde5efd Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Fri, 6 Jan 2023 10:21:28 +0000 Subject: [PATCH 24/33] Fix on-heap aux stack allocation (#1865) Because stack grows from high address towards low address, the value returned by malloc is the end of the stack, not top of the stack. The top of the stack is the end of the allocated space (i.e. address returned by malloc + cluster size). Refer to #1790. --- core/iwasm/libraries/thread-mgr/thread_manager.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index ede36ebe2..611414a43 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -83,11 +83,14 @@ allocate_aux_stack(WASMExecEnv *exec_env, uint32 *start, uint32 *size) #if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 WASMModuleInstanceCommon *module_inst = wasm_exec_env_get_module_inst(exec_env); + uint32 stack_end; - *start = wasm_runtime_module_malloc(module_inst, cluster->stack_size, NULL); + stack_end = + wasm_runtime_module_malloc(module_inst, cluster->stack_size, NULL); + *start = stack_end + cluster->stack_size; *size = cluster->stack_size; - return *start != 0; + return stack_end != 0; #else uint32 i; @@ -116,15 +119,18 @@ allocate_aux_stack(WASMExecEnv *exec_env, uint32 *start, uint32 *size) static bool free_aux_stack(WASMExecEnv *exec_env, uint32 start) { + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + #if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION != 0 WASMModuleInstanceCommon *module_inst = wasm_exec_env_get_module_inst(exec_env); - wasm_runtime_module_free(module_inst, start); + bh_assert(start >= cluster->stack_size); + + wasm_runtime_module_free(module_inst, start - cluster->stack_size); return true; #else - WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); uint32 i; for (i = 0; i < cluster_max_thread_num; i++) { From 0e2382a95917dec8f4eaa89501151dc50d1488d4 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Mon, 9 Jan 2023 12:36:34 +0000 Subject: [PATCH 25/33] Disable aux stack allocations for threads spawned by wasi_thread_start (#1867) This syscall doesn't need allocating stack or TLS and it's expected from the application to do that instead. E.g. WASI-libc already does this for `pthread_create`. Also fix some of the examples to allocate memory for stack and not use stack before the stack pointer is set to a correct value. --- core/iwasm/common/wasm_exec_env.h | 7 +++ core/iwasm/interpreter/wasm_interp_classic.c | 20 +++++---- core/iwasm/interpreter/wasm_interp_fast.c | 20 +++++---- .../lib-pthread/lib_pthread_wrapper.c | 5 ++- .../lib_wasi_threads_wrapper.c | 4 +- .../libraries/thread-mgr/thread_manager.c | 29 +++++++----- .../libraries/thread-mgr/thread_manager.h | 2 +- samples/wasi-threads/wasm-apps/CMakeLists.txt | 7 +-- .../wasm-apps/exception_propagation.c | 44 +++++++++++++------ samples/wasi-threads/wasm-apps/no_pthread.c | 27 +++++++++--- .../wasm-apps/wasi_thread_start.S | 22 ++++++++++ .../wasm-apps/wasi_thread_start.h | 32 ++++++++++++++ 12 files changed, 163 insertions(+), 56 deletions(-) create mode 100644 samples/wasi-threads/wasm-apps/wasi_thread_start.S create mode 100644 samples/wasi-threads/wasm-apps/wasi_thread_start.h diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index 398292079..72f973e7b 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -179,6 +179,13 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, void wasm_exec_env_destroy(WASMExecEnv *exec_env); +static inline bool +wasm_exec_env_is_aux_stack_managed_by_runtime(WASMExecEnv *exec_env) +{ + return exec_env->aux_stack_boundary.boundary != 0 + || exec_env->aux_stack_bottom.bottom != 0; +} + /** * Allocate a WASM frame from the WASM stack. * diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index fa542b14e..d9cb3f5f3 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1783,14 +1783,18 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, global = globals + global_idx; global_addr = get_global_addr(global_data, global); aux_stack_top = *(uint32 *)(frame_sp - 1); - if (aux_stack_top <= exec_env->aux_stack_boundary.boundary) { - wasm_set_exception(module, "wasm auxiliary stack overflow"); - goto got_exception; - } - if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { - wasm_set_exception(module, - "wasm auxiliary stack underflow"); - goto got_exception; + if (wasm_exec_env_is_aux_stack_managed_by_runtime(exec_env)) { + if (aux_stack_top + <= exec_env->aux_stack_boundary.boundary) { + wasm_set_exception(module, + "wasm auxiliary stack overflow"); + goto got_exception; + } + if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { + wasm_set_exception(module, + "wasm auxiliary stack underflow"); + goto got_exception; + } } *(int32 *)global_addr = aux_stack_top; frame_sp--; diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 3109b0c82..4f1ae3c7c 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1576,14 +1576,18 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, global = globals + global_idx; global_addr = get_global_addr(global_data, global); aux_stack_top = frame_lp[GET_OFFSET()]; - if (aux_stack_top <= exec_env->aux_stack_boundary.boundary) { - wasm_set_exception(module, "wasm auxiliary stack overflow"); - goto got_exception; - } - if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { - wasm_set_exception(module, - "wasm auxiliary stack underflow"); - goto got_exception; + if (wasm_exec_env_is_aux_stack_managed_by_runtime(exec_env)) { + if (aux_stack_top + <= exec_env->aux_stack_boundary.boundary) { + wasm_set_exception(module, + "wasm auxiliary stack overflow"); + goto got_exception; + } + if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { + wasm_set_exception(module, + "wasm auxiliary stack underflow"); + goto got_exception; + } } *(int32 *)global_addr = aux_stack_top; #if WASM_ENABLE_MEMORY_PROFILING != 0 diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 9f5a9a8f3..9ab7848c4 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -619,8 +619,9 @@ pthread_create_wrapper(wasm_exec_env_t exec_env, routine_args->module_inst = new_module_inst; os_mutex_lock(&exec_env->wait_lock); - ret = wasm_cluster_create_thread( - exec_env, new_module_inst, pthread_start_routine, (void *)routine_args); + ret = + wasm_cluster_create_thread(exec_env, new_module_inst, true, + pthread_start_routine, (void *)routine_args); if (ret != 0) { os_mutex_unlock(&exec_env->wait_lock); goto fail; diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 846c908c9..044e5d22e 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -126,8 +126,8 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg) thread_start_arg->start_func = start_func; os_mutex_lock(&exec_env->wait_lock); - ret = wasm_cluster_create_thread(exec_env, new_module_inst, thread_start, - thread_start_arg); + ret = wasm_cluster_create_thread(exec_env, new_module_inst, false, + thread_start, thread_start_arg); if (ret != 0) { LOG_ERROR("Failed to spawn a new thread"); goto thread_spawn_fail; diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 611414a43..e2a0df8be 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -125,6 +125,10 @@ free_aux_stack(WASMExecEnv *exec_env, uint32 start) WASMModuleInstanceCommon *module_inst = wasm_exec_env_get_module_inst(exec_env); + if (!wasm_exec_env_is_aux_stack_managed_by_runtime(exec_env)) { + return true; + } + bh_assert(start >= cluster->stack_size); wasm_runtime_module_free(module_inst, start - cluster->stack_size); @@ -534,7 +538,7 @@ thread_manager_start_routine(void *arg) int32 wasm_cluster_create_thread(WASMExecEnv *exec_env, - wasm_module_inst_t module_inst, + wasm_module_inst_t module_inst, bool alloc_aux_stack, void *(*thread_routine)(void *), void *arg) { WASMCluster *cluster; @@ -550,16 +554,18 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, if (!new_exec_env) return -1; - if (!allocate_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { - LOG_ERROR("thread manager error: " - "failed to allocate aux stack space for new thread"); - goto fail1; - } + if (alloc_aux_stack) { + if (!allocate_aux_stack(exec_env, &aux_stack_start, &aux_stack_size)) { + LOG_ERROR("thread manager error: " + "failed to allocate aux stack space for new thread"); + goto fail1; + } - /* Set aux stack for current thread */ - if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, - aux_stack_size)) { - goto fail2; + /* Set aux stack for current thread */ + if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, + aux_stack_size)) { + goto fail2; + } } if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) @@ -581,7 +587,8 @@ fail3: wasm_cluster_del_exec_env(cluster, new_exec_env); fail2: /* free the allocated aux stack space */ - free_aux_stack(exec_env, aux_stack_start); + if (alloc_aux_stack) + free_aux_stack(exec_env, aux_stack_start); fail1: wasm_exec_env_destroy(new_exec_env); return -1; diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h index aa1a9154b..54e71ce72 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.h +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -64,7 +64,7 @@ wasm_exec_env_get_cluster(WASMExecEnv *exec_env); int32 wasm_cluster_create_thread(WASMExecEnv *exec_env, - wasm_module_inst_t module_inst, + wasm_module_inst_t module_inst, bool alloc_aux_stack, void *(*thread_routine)(void *), void *arg); int32 diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt index 59f48713b..b2d763871 100644 --- a/samples/wasi-threads/wasm-apps/CMakeLists.txt +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -13,12 +13,13 @@ endif () set (CMAKE_SYSROOT "${WASI_SYSROOT}") set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") +set (CMAKE_ASM_COMPILER "${WASI_SDK_DIR}/bin/clang") set (CMAKE_C_COMPILER_TARGET "wasm32-wasi") function (compile_sample SOURCE_FILE) get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) set (WASM_MODULE ${FILE_NAME}.wasm) - add_executable (${WASM_MODULE} ${SOURCE_FILE}) + add_executable (${WASM_MODULE} ${SOURCE_FILE} ${ARGN}) target_compile_options (${WASM_MODULE} PRIVATE -pthread -ftls-model=local-exec) @@ -34,5 +35,5 @@ function (compile_sample SOURCE_FILE) ) endfunction () -compile_sample(no_pthread.c) -compile_sample(exception_propagation.c) +compile_sample(no_pthread.c wasi_thread_start.S) +compile_sample(exception_propagation.c wasi_thread_start.S) diff --git a/samples/wasi-threads/wasm-apps/exception_propagation.c b/samples/wasi-threads/wasm-apps/exception_propagation.c index 91f9f3560..9e07675be 100644 --- a/samples/wasi-threads/wasm-apps/exception_propagation.c +++ b/samples/wasi-threads/wasm-apps/exception_propagation.c @@ -9,15 +9,21 @@ #include #include #include -#include #include #include #include +#include "wasi_thread_start.h" + #define TIMEOUT_SECONDS 10 #define NUM_THREADS 3 static sem_t sem; +typedef struct { + start_args_t base; + bool throw_exception; +} shared_t; + void run_long_task() { @@ -26,12 +32,12 @@ run_long_task() sleep(1); } -__attribute__((export_name("wasi_thread_start"))) void -wasi_thread_start(int thread_id, int *start_arg) +void +__wasi_thread_start_C(int thread_id, int *start_arg) { - bool has_to_throw_exception = (bool)start_arg; + shared_t *data = (shared_t *)start_arg; - if (has_to_throw_exception) { + if (data->throw_exception) { // Wait for all other threads (including main thread) to be ready printf("Waiting before throwing exception\n"); for (int i = 0; i < NUM_THREADS; i++) @@ -52,26 +58,36 @@ wasi_thread_start(int thread_id, int *start_arg) int main(int argc, char **argv) { - int thread_id = -1; + int thread_id = -1, i; + shared_t data[NUM_THREADS] = { 0 }; + if (sem_init(&sem, 0, 0) != 0) { printf("Failed to init semaphore\n"); return EXIT_FAILURE; } - // Create a thread that throws an exception - thread_id = __wasi_thread_spawn((void *)true); - if (thread_id < 0) { - printf("Failed to create thread: %d\n", thread_id); - return EXIT_FAILURE; + for (i = 0; i < NUM_THREADS; i++) { + // No graceful memory free to simplify the example + if (!start_args_init(&data[i].base)) { + printf("Failed to allocate thread's stack\n"); + return EXIT_FAILURE; + } } - // Create two additional threads to test exception propagation - thread_id = __wasi_thread_spawn((void *)false); + // Create a thread that throws an exception + data[0].throw_exception = true; + thread_id = __wasi_thread_spawn(&data[0]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; } - thread_id = __wasi_thread_spawn((void *)false); + // Create two additional threads to test exception propagation + thread_id = __wasi_thread_spawn(&data[1]); + if (thread_id < 0) { + printf("Failed to create thread: %d\n", thread_id); + return EXIT_FAILURE; + } + thread_id = __wasi_thread_spawn(&data[2]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; diff --git a/samples/wasi-threads/wasm-apps/no_pthread.c b/samples/wasi-threads/wasm-apps/no_pthread.c index 6eb605004..fcf64a7b7 100644 --- a/samples/wasi-threads/wasm-apps/no_pthread.c +++ b/samples/wasi-threads/wasm-apps/no_pthread.c @@ -9,18 +9,20 @@ #include #include #include -#include + +#include "wasi_thread_start.h" static const int64_t SECOND = 1000 * 1000 * 1000; typedef struct { + start_args_t base; int th_ready; int value; int thread_id; } shared_t; -__attribute__((export_name("wasi_thread_start"))) void -wasi_thread_start(int thread_id, int *start_arg) +void +__wasi_thread_start_C(int thread_id, int *start_arg) { shared_t *data = (shared_t *)start_arg; @@ -38,18 +40,26 @@ wasi_thread_start(int thread_id, int *start_arg) int main(int argc, char **argv) { - shared_t data = { 0, 52, -1 }; + shared_t data = { { NULL }, 0, 52, -1 }; int thread_id; + int ret = EXIT_SUCCESS; + + if (!start_args_init(&data.base)) { + printf("Stack allocation for thread failed\n"); + return EXIT_FAILURE; + } thread_id = __wasi_thread_spawn(&data); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); - return EXIT_FAILURE; + ret = EXIT_FAILURE; + goto final; } if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) { printf("Timeout\n"); - return EXIT_FAILURE; + ret = EXIT_FAILURE; + goto final; } printf("Thread completed, new value: %d, thread id: %d\n", data.value, @@ -57,5 +67,8 @@ main(int argc, char **argv) assert(thread_id == data.thread_id); - return EXIT_SUCCESS; +final: + start_args_deinit(&data.base); + + return ret; } diff --git a/samples/wasi-threads/wasm-apps/wasi_thread_start.S b/samples/wasi-threads/wasm-apps/wasi_thread_start.S new file mode 100644 index 000000000..ea8fd1400 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/wasi_thread_start.S @@ -0,0 +1,22 @@ +# A slightly modified copy of the wasi-libc implementation +# https://github.com/WebAssembly/wasi-libc/pull/376/ + .globaltype __stack_pointer, i32 + .functype __wasi_thread_start_C (i32, i32) -> () + + .globl wasi_thread_start + +wasi_thread_start: + .functype wasi_thread_start (i32, i32) -> () + + # Set up the minimum C environment. + # Note: offsetof(start_arg, stack) == 0 + local.get 1 # start_arg + i32.load 0 # stack + global.set __stack_pointer + + # Make the C function do the rest of work. + local.get 0 # tid + local.get 1 # start_arg + call __wasi_thread_start_C + + end_function \ No newline at end of file diff --git a/samples/wasi-threads/wasm-apps/wasi_thread_start.h b/samples/wasi-threads/wasm-apps/wasi_thread_start.h new file mode 100644 index 000000000..651f13c6c --- /dev/null +++ b/samples/wasi-threads/wasm-apps/wasi_thread_start.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef WASI_THREAD_START_H +#define WASI_THREAD_START_H + +#define STACK_SIZE 1024 + +typedef struct { + void *stack; +} start_args_t; + +static inline int +start_args_init(start_args_t *start_args) +{ + start_args->stack = malloc(STACK_SIZE); + if (!start_args->stack) { + return 0; + } + + start_args->stack += STACK_SIZE; + return 1; +} + +static inline void +start_args_deinit(start_args_t *start_args) +{ + free(start_args->stack - STACK_SIZE); +} + +#endif \ No newline at end of file From 903f5212f194c2709b32631bd32936fdcfed27af Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Wed, 11 Jan 2023 01:40:32 +0000 Subject: [PATCH 26/33] Increase stack size in the example app (#1880) --- samples/wasi-threads/wasm-apps/wasi_thread_start.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/wasi-threads/wasm-apps/wasi_thread_start.h b/samples/wasi-threads/wasm-apps/wasi_thread_start.h index 651f13c6c..a46917d0a 100644 --- a/samples/wasi-threads/wasm-apps/wasi_thread_start.h +++ b/samples/wasi-threads/wasm-apps/wasi_thread_start.h @@ -5,7 +5,7 @@ #ifndef WASI_THREAD_START_H #define WASI_THREAD_START_H -#define STACK_SIZE 1024 +#define STACK_SIZE 32 * 1024 // same as the main stack typedef struct { void *stack; From 5f0f8fe7ee04bbc9e66df2a604a06b241f2fcc97 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 16 Jan 2023 21:21:51 +0900 Subject: [PATCH 27/33] Relax wasi memory export check when wasi-threads is enabled (#1893) https://github.com/WebAssembly/wasi-threads/issues/22 https://github.com/WebAssembly/WASI/issues/502 --- core/iwasm/interpreter/wasm_loader.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index d0a10b86e..753f837da 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -4185,7 +4185,20 @@ check_wasi_abi_compatibility(const WASMModule *module, memory = wasm_loader_find_export(module, "", "memory", EXPORT_KIND_MEMORY, error_buf, error_buf_size); - if (!memory) { + if (!memory +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + /* + * with wasi-threads, it's still an open question if a memory + * should be exported. + * + * https://github.com/WebAssembly/wasi-threads/issues/22 + * https://github.com/WebAssembly/WASI/issues/502 + * + * Note: this code assumes the number of memories is at most 1. + */ + && module->import_memory_count == 0 +#endif + ) { set_error_buf(error_buf, error_buf_size, "a module with WASI apis must export memory by default"); return false; From 9cf55f953b301d41db31dbc494bbfb5e4cf6571b Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Mon, 23 Jan 2023 03:36:58 +0100 Subject: [PATCH 28/33] feat(wasi-threads): Change sample exception_propagation to thread_termination (#1869) Trigger wasi_proc_exit in the sample --- samples/wasi-threads/wasm-apps/CMakeLists.txt | 2 +- ...ion_propagation.c => thread_termination.c} | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) rename samples/wasi-threads/wasm-apps/{exception_propagation.c => thread_termination.c} (68%) diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt index b2d763871..0da9aa749 100644 --- a/samples/wasi-threads/wasm-apps/CMakeLists.txt +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -36,4 +36,4 @@ function (compile_sample SOURCE_FILE) endfunction () compile_sample(no_pthread.c wasi_thread_start.S) -compile_sample(exception_propagation.c wasi_thread_start.S) +compile_sample(thread_termination.c wasi_thread_start.S) \ No newline at end of file diff --git a/samples/wasi-threads/wasm-apps/exception_propagation.c b/samples/wasi-threads/wasm-apps/thread_termination.c similarity index 68% rename from samples/wasi-threads/wasm-apps/exception_propagation.c rename to samples/wasi-threads/wasm-apps/thread_termination.c index 9e07675be..2adaa621f 100644 --- a/samples/wasi-threads/wasm-apps/exception_propagation.c +++ b/samples/wasi-threads/wasm-apps/thread_termination.c @@ -15,6 +15,9 @@ #include "wasi_thread_start.h" +#define TEST_TERMINATION_BY_TRAP 0 // Otherwise test `proc_exit` termination +#define TEST_TERMINATION_IN_MAIN_THREAD 1 + #define TIMEOUT_SECONDS 10 #define NUM_THREADS 3 static sem_t sem; @@ -27,7 +30,7 @@ typedef struct { void run_long_task() { - // Busy waiting to be interruptible by exception + // Busy waiting to be interruptible by trap or `proc_exit` for (int i = 0; i < TIMEOUT_SECONDS; i++) sleep(1); } @@ -39,18 +42,22 @@ __wasi_thread_start_C(int thread_id, int *start_arg) if (data->throw_exception) { // Wait for all other threads (including main thread) to be ready - printf("Waiting before throwing exception\n"); + printf("Waiting before terminating\n"); for (int i = 0; i < NUM_THREADS; i++) sem_wait(&sem); - printf("Throwing exception\n"); + printf("Force termination\n"); +#if TEST_TERMINATION_BY_TRAP == 1 __builtin_trap(); +#else + __wasi_proc_exit(1); +#endif } else { printf("Thread running\n"); sem_post(&sem); - run_long_task(); // Wait to be interrupted by exception + run_long_task(); // Wait to be interrupted assert(false && "Unreachable"); } } @@ -74,19 +81,26 @@ main(int argc, char **argv) } } - // Create a thread that throws an exception +// Create a thread that forces termination through trap or `proc_exit` +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + data[0].throw_exception = false; +#else data[0].throw_exception = true; +#endif thread_id = __wasi_thread_spawn(&data[0]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; } + // Create two additional threads to test exception propagation + data[1].throw_exception = false; thread_id = __wasi_thread_spawn(&data[1]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); return EXIT_FAILURE; } + data[2].throw_exception = false; thread_id = __wasi_thread_spawn(&data[2]); if (thread_id < 0) { printf("Failed to create thread: %d\n", thread_id); @@ -96,8 +110,19 @@ main(int argc, char **argv) printf("Main thread running\n"); sem_post(&sem); - run_long_task(); // Wait to be interrupted by exception - assert(false && "Unreachable"); +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + + printf("Force termination (main thread)\n"); +#if TEST_TERMINATION_BY_TRAP == 1 + __builtin_trap(); +#else /* TEST_TERMINATION_BY_TRAP */ + __wasi_proc_exit(1); +#endif /* TEST_TERMINATION_BY_TRAP */ + +#else /* TEST_TERMINATION_IN_MAIN_THREAD */ + run_long_task(); // Wait to be interrupted + assert(false && "Unreachable"); +#endif /* TEST_TERMINATION_IN_MAIN_THREAD */ return EXIT_SUCCESS; } \ No newline at end of file From 879563047fbc47afb638588c609b9005faaa3c44 Mon Sep 17 00:00:00 2001 From: Marcin Kolny Date: Wed, 25 Jan 2023 01:34:36 +0000 Subject: [PATCH 29/33] Fix thread termination example (#1915) The example wasn't fully implemented the intention - it didn't work as expected when the trap/proc_exit was executed on the main thread, because main thread never waited for all the threads to start. --- .../wasm-apps/thread_termination.c | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/samples/wasi-threads/wasm-apps/thread_termination.c b/samples/wasi-threads/wasm-apps/thread_termination.c index 2adaa621f..dfc228eb3 100644 --- a/samples/wasi-threads/wasm-apps/thread_termination.c +++ b/samples/wasi-threads/wasm-apps/thread_termination.c @@ -35,30 +35,42 @@ run_long_task() sleep(1); } +void +start_job() +{ + sem_post(&sem); + run_long_task(); // Wait to be interrupted + assert(false && "Unreachable"); +} + +void +terminate_process() +{ + // Wait for all other threads (including main thread) to be ready + printf("Waiting before terminating\n"); + for (int i = 0; i < NUM_THREADS; i++) + sem_wait(&sem); + + printf("Force termination\n"); +#if TEST_TERMINATION_BY_TRAP == 1 + __builtin_trap(); +#else + __wasi_proc_exit(1); +#endif +} + void __wasi_thread_start_C(int thread_id, int *start_arg) { shared_t *data = (shared_t *)start_arg; if (data->throw_exception) { - // Wait for all other threads (including main thread) to be ready - printf("Waiting before terminating\n"); - for (int i = 0; i < NUM_THREADS; i++) - sem_wait(&sem); - - printf("Force termination\n"); -#if TEST_TERMINATION_BY_TRAP == 1 - __builtin_trap(); -#else - __wasi_proc_exit(1); -#endif + terminate_process(); } else { printf("Thread running\n"); - sem_post(&sem); - run_long_task(); // Wait to be interrupted - assert(false && "Unreachable"); + start_job(); } } @@ -107,22 +119,13 @@ main(int argc, char **argv) return EXIT_FAILURE; } +#if TEST_TERMINATION_IN_MAIN_THREAD == 1 + printf("Force termination (main thread)\n"); + terminate_process(); +#else /* TEST_TERMINATION_IN_MAIN_THREAD */ printf("Main thread running\n"); - sem_post(&sem); - -#if TEST_TERMINATION_IN_MAIN_THREAD == 1 - - printf("Force termination (main thread)\n"); -#if TEST_TERMINATION_BY_TRAP == 1 - __builtin_trap(); -#else /* TEST_TERMINATION_BY_TRAP */ - __wasi_proc_exit(1); -#endif /* TEST_TERMINATION_BY_TRAP */ - -#else /* TEST_TERMINATION_IN_MAIN_THREAD */ - run_long_task(); // Wait to be interrupted - assert(false && "Unreachable"); + start_job(); #endif /* TEST_TERMINATION_IN_MAIN_THREAD */ return EXIT_SUCCESS; } \ No newline at end of file From 42f8fed20ec6f04213572d142803ba3e0c5d53e6 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 27 Jan 2023 07:45:34 +0900 Subject: [PATCH 30/33] Rename thread_spawn import (#1907) Following the wit-defined ABI: https://github.com/WebAssembly/wasi-threads/pull/26 cf. https://github.com/WebAssembly/wasi-libc/pull/387 --- .github/workflows/compilation_on_android_ubuntu.yml | 7 ++++++- .github/workflows/compilation_on_macos.yml | 9 +++++++-- .github/workflows/compilation_on_sgx.yml | 7 ++++++- .../lib-wasi-threads/lib_wasi_threads_wrapper.c | 6 +++--- samples/wasi-threads/wasm-apps/CMakeLists.txt | 4 ++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index bf31c499f..b8e2edd23 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -326,8 +326,13 @@ jobs: - name: build wasi-libc (needed for wasi-threads) run: | - git clone --branch wasi-sdk-17 https://github.com/WebAssembly/wasi-libc + mkdir wasi-libc cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD make \ AR=/opt/wasi-sdk/bin/llvm-ar \ NM=/opt/wasi-sdk/bin/llvm-nm \ diff --git a/.github/workflows/compilation_on_macos.yml b/.github/workflows/compilation_on_macos.yml index 1e5ff415b..31bd424ba 100644 --- a/.github/workflows/compilation_on_macos.yml +++ b/.github/workflows/compilation_on_macos.yml @@ -268,8 +268,13 @@ jobs: - name: build wasi-libc (needed for wasi-threads) run: | - git clone --branch wasi-sdk-17 https://github.com/WebAssembly/wasi-libc + mkdir wasi-libc cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD make \ AR=/opt/wasi-sdk/bin/llvm-ar \ NM=/opt/wasi-sdk/bin/llvm-nm \ @@ -329,4 +334,4 @@ jobs: mkdir build && cd build cmake -DWASI_SYSROOT=`pwd`/../../../core/deps/wasi-libc/sysroot .. cmake --build . --config Release --parallel 4 - ./iwasm wasm-apps/no_pthread.wasm \ No newline at end of file + ./iwasm wasm-apps/no_pthread.wasm diff --git a/.github/workflows/compilation_on_sgx.yml b/.github/workflows/compilation_on_sgx.yml index c4d4142dd..efcb294ae 100644 --- a/.github/workflows/compilation_on_sgx.yml +++ b/.github/workflows/compilation_on_sgx.yml @@ -270,8 +270,13 @@ jobs: - name: build wasi-libc (needed for wasi-threads) run: | - git clone --branch wasi-sdk-17 https://github.com/WebAssembly/wasi-libc + mkdir wasi-libc cd wasi-libc + git init + # "Rename thread_spawn import" commit on main branch + git fetch https://github.com/WebAssembly/wasi-libc \ + 8f5275796a82f8ecfd0833a4f3f444fa37ed4546 + git checkout FETCH_HEAD make \ AR=/opt/wasi-sdk/bin/llvm-ar \ NM=/opt/wasi-sdk/bin/llvm-nm \ diff --git a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c index 46db6d524..81efc6751 100644 --- a/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -148,12 +148,12 @@ thread_preparation_fail: } /* clang-format off */ -#define REG_NATIVE_FUNC(func_name, signature) \ - { #func_name, func_name##_wrapper, signature, NULL } +#define REG_NATIVE_FUNC(name, func_name, signature) \ + { name, func_name##_wrapper, signature, NULL } /* clang-format on */ static NativeSymbol native_symbols_lib_wasi_threads[] = { REG_NATIVE_FUNC( - thread_spawn, "(i)i") }; + "thread-spawn", thread_spawn, "(i)i") }; uint32 get_lib_wasi_threads_export_apis(NativeSymbol **p_lib_wasi_threads_apis) diff --git a/samples/wasi-threads/wasm-apps/CMakeLists.txt b/samples/wasi-threads/wasm-apps/CMakeLists.txt index 0da9aa749..c58697868 100644 --- a/samples/wasi-threads/wasm-apps/CMakeLists.txt +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -14,7 +14,7 @@ endif () set (CMAKE_SYSROOT "${WASI_SYSROOT}") set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") set (CMAKE_ASM_COMPILER "${WASI_SDK_DIR}/bin/clang") -set (CMAKE_C_COMPILER_TARGET "wasm32-wasi") +set (CMAKE_EXE_LINKER_FLAGS "-target wasm32-wasi-threads") function (compile_sample SOURCE_FILE) get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) @@ -36,4 +36,4 @@ function (compile_sample SOURCE_FILE) endfunction () compile_sample(no_pthread.c wasi_thread_start.S) -compile_sample(thread_termination.c wasi_thread_start.S) \ No newline at end of file +compile_sample(thread_termination.c wasi_thread_start.S) From 2eed50b3034b8eec531cce4a782b8c698fea00fd Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Fri, 27 Jan 2023 11:32:33 +0100 Subject: [PATCH 31/33] Document how to use WASI threads in AOT mode (#1905) Describe how to use WASI threads in AOT mode, following the discussion below: https://github.com/bytecodealliance/wasm-micro-runtime/pull/1867#discussion_r1070268062 Make aux stack boundary checks of wasi-threads always successful by setting `exec_env->aux_stack_bottom` to UINT32_MAX and `exec_env->aux_stack_boundary` to 0 --- core/iwasm/interpreter/wasm_interp_classic.c | 20 ++++++++----------- core/iwasm/interpreter/wasm_interp_fast.c | 20 ++++++++----------- .../libraries/thread-mgr/thread_manager.c | 5 +++++ samples/wasi-threads/README.md | 9 ++++++++- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 06d75a489..3e16b3803 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1780,18 +1780,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, global = globals + global_idx; global_addr = get_global_addr(global_data, global); aux_stack_top = *(uint32 *)(frame_sp - 1); - if (wasm_exec_env_is_aux_stack_managed_by_runtime(exec_env)) { - if (aux_stack_top - <= exec_env->aux_stack_boundary.boundary) { - wasm_set_exception(module, - "wasm auxiliary stack overflow"); - goto got_exception; - } - if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { - wasm_set_exception(module, - "wasm auxiliary stack underflow"); - goto got_exception; - } + if (aux_stack_top <= exec_env->aux_stack_boundary.boundary) { + wasm_set_exception(module, "wasm auxiliary stack overflow"); + goto got_exception; + } + if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { + wasm_set_exception(module, + "wasm auxiliary stack underflow"); + goto got_exception; } *(int32 *)global_addr = aux_stack_top; frame_sp--; diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index adcb18c2a..0ea920f23 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1576,18 +1576,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, global = globals + global_idx; global_addr = get_global_addr(global_data, global); aux_stack_top = frame_lp[GET_OFFSET()]; - if (wasm_exec_env_is_aux_stack_managed_by_runtime(exec_env)) { - if (aux_stack_top - <= exec_env->aux_stack_boundary.boundary) { - wasm_set_exception(module, - "wasm auxiliary stack overflow"); - goto got_exception; - } - if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { - wasm_set_exception(module, - "wasm auxiliary stack underflow"); - goto got_exception; - } + if (aux_stack_top <= exec_env->aux_stack_boundary.boundary) { + wasm_set_exception(module, "wasm auxiliary stack overflow"); + goto got_exception; + } + if (aux_stack_top > exec_env->aux_stack_bottom.bottom) { + wasm_set_exception(module, + "wasm auxiliary stack underflow"); + goto got_exception; } *(int32 *)global_addr = aux_stack_top; #if WASM_ENABLE_MEMORY_PROFILING != 0 diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index 492ed66b1..8ec310d92 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -586,6 +586,11 @@ wasm_cluster_create_thread(WASMExecEnv *exec_env, goto fail3; } } + else { + /* Disable aux stack */ + new_exec_env->aux_stack_boundary.boundary = 0; + new_exec_env->aux_stack_bottom.bottom = UINT32_MAX; + } if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) goto fail3; diff --git a/samples/wasi-threads/README.md b/samples/wasi-threads/README.md index 533f687ea..499162b6e 100644 --- a/samples/wasi-threads/README.md +++ b/samples/wasi-threads/README.md @@ -10,7 +10,7 @@ make \ THREAD_MODEL=posix ``` -Build and run the samples +## Build and run the samples ```shell $ mkdir build @@ -22,3 +22,10 @@ $ ./iwasm wasm-apps/no_pthread.wasm ... $ ./iwasm wasm-apps/exception_propagation.wasm ``` + +## Run samples in AOT mode +```shell +$ ../../../wamr-compiler/build/wamrc \ + -o wasm-apps/no_pthread.aot wasm-apps/no_pthread.wasm +$ ./iwasm wasm-apps/no_pthread.aot +``` From aab875b91f23d9c9f12ea92a4f4042702ec1e243 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 7 Feb 2023 13:34:33 +0900 Subject: [PATCH 32/33] wamr-compiler: enable wasi-threads (#1938) cf. https://github.com/bytecodealliance/wasm-micro-runtime/pull/1893 --- wamr-compiler/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wamr-compiler/CMakeLists.txt b/wamr-compiler/CMakeLists.txt index 75c85a77a..18d65c075 100644 --- a/wamr-compiler/CMakeLists.txt +++ b/wamr-compiler/CMakeLists.txt @@ -44,6 +44,7 @@ add_definitions(-DWASM_ENABLE_CUSTOM_NAME_SECTION=1) add_definitions(-DWASM_ENABLE_DUMP_CALL_STACK=1) add_definitions(-DWASM_ENABLE_PERF_PROFILING=1) add_definitions(-DWASM_ENABLE_LOAD_CUSTOM_SECTION=1) +add_definitions(-DWASM_ENABLE_LIB_WASI_THREADS=1) if (WAMR_BUILD_LLVM_LEGACY_PM EQUAL 1) add_definitions(-DWASM_ENABLE_LLVM_LEGACY_PM=1) @@ -204,6 +205,7 @@ if (NOT MINGW) endif() endif() include (${IWASM_DIR}/libraries/lib-pthread/lib_pthread.cmake) +include (${IWASM_DIR}/libraries/lib-wasi-threads/lib_wasi_threads.cmake) include (${IWASM_DIR}/common/iwasm_common.cmake) include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) include (${IWASM_DIR}/aot/iwasm_aot.cmake) @@ -258,6 +260,7 @@ add_library (vmlib ${LIBC_BUILTIN_SOURCE} ${LIBC_WASI_SOURCE} ${LIB_PTHREAD_SOURCE} + ${LIB_WASI_THREADS_SOURCE} ${IWASM_COMMON_SOURCE} ${IWASM_INTERP_SOURCE} ${IWASM_AOT_SOURCE}) From ee1871d3f857a056593ca0d390ce25b6ae492feb Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Tue, 7 Feb 2023 14:46:41 +0900 Subject: [PATCH 33/33] Enable iwasm --max-threads option for wasi-threads as well (#1939) --- product-mini/platforms/posix/main.c | 4 ++-- product-mini/platforms/windows/main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index e04745c68..06fd32092 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -67,7 +67,7 @@ print_help() printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); #endif #if WASM_ENABLE_DEBUG_INTERP != 0 @@ -499,7 +499,7 @@ main(int argc, char *argv[]) } } #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 else if (!strncmp(argv[0], "--max-threads=", 14)) { if (argv[0][14] == '\0') return print_help(); diff --git a/product-mini/platforms/windows/main.c b/product-mini/platforms/windows/main.c index 14c255961..6de76ccae 100644 --- a/product-mini/platforms/windows/main.c +++ b/product-mini/platforms/windows/main.c @@ -43,7 +43,7 @@ print_help() printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); #endif #if WASM_ENABLE_DEBUG_INTERP != 0 @@ -317,7 +317,7 @@ main(int argc, char *argv[]) } } #endif -#if WASM_ENABLE_LIB_PTHREAD != 0 +#if WASM_ENABLE_LIB_PTHREAD != 0 || WASM_ENABLE_LIB_WASI_THREADS != 0 else if (!strncmp(argv[0], "--max-threads=", 14)) { if (argv[0][14] == '\0') return print_help();