Refine AOT exception check when function return (#1752)

Refine AOT exception check in the caller when returning from callee function,
remove the exception check instructions when hw bound check is enabled to
improve the performance: create guard page to trigger signal handler when
exception occurs.
This commit is contained in:
Wenyong Huang 2022-11-30 20:18:28 +08:00 committed by GitHub
parent 7cb1ebc771
commit ce3458da99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 163 additions and 50 deletions

View File

@ -1500,7 +1500,11 @@ aot_set_exception(AOTModuleInstance *module_inst, const char *exception)
void void
aot_set_exception_with_id(AOTModuleInstance *module_inst, uint32 id) aot_set_exception_with_id(AOTModuleInstance *module_inst, uint32 id)
{ {
wasm_set_exception_with_id(module_inst, id); if (id != EXCE_ALREADY_THROWN)
wasm_set_exception_with_id(module_inst, id);
#ifdef OS_ENABLE_HW_BOUND_CHECK
wasm_runtime_access_exce_check_guard_page();
#endif
} }
const char * const char *
@ -1755,6 +1759,7 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc,
const char *signature; const char *signature;
void *attachment; void *attachment;
char buf[96]; char buf[96];
bool ret = false;
bh_assert(func_idx < aot_module->import_func_count); bh_assert(func_idx < aot_module->import_func_count);
@ -1764,27 +1769,34 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc,
"failed to call unlinked import function (%s, %s)", "failed to call unlinked import function (%s, %s)",
import_func->module_name, import_func->func_name); import_func->module_name, import_func->func_name);
aot_set_exception(module_inst, buf); aot_set_exception(module_inst, buf);
return false; goto fail;
} }
attachment = import_func->attachment; attachment = import_func->attachment;
if (import_func->call_conv_wasm_c_api) { if (import_func->call_conv_wasm_c_api) {
return wasm_runtime_invoke_c_api_native( ret = wasm_runtime_invoke_c_api_native(
(WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc, (WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc,
argv, import_func->wasm_c_api_with_env, attachment); argv, import_func->wasm_c_api_with_env, attachment);
} }
else if (!import_func->call_conv_raw) { else if (!import_func->call_conv_raw) {
signature = import_func->signature; signature = import_func->signature;
return wasm_runtime_invoke_native(exec_env, func_ptr, func_type, ret =
signature, attachment, argv, argc, wasm_runtime_invoke_native(exec_env, func_ptr, func_type, signature,
argv); attachment, argv, argc, argv);
} }
else { else {
signature = import_func->signature; signature = import_func->signature;
return wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type, ret = wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type,
signature, attachment, argv, argc, signature, attachment, argv, argc,
argv); argv);
} }
fail:
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!ret)
wasm_runtime_access_exce_check_guard_page();
#endif
return ret;
} }
bool bool
@ -1811,7 +1823,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
if ((uint8 *)&module_inst < exec_env->native_stack_boundary) { if ((uint8 *)&module_inst < exec_env->native_stack_boundary) {
aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW);
return false; goto fail;
} }
tbl_inst = module_inst->tables[tbl_idx]; tbl_inst = module_inst->tables[tbl_idx];
@ -1819,13 +1831,13 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
if (table_elem_idx >= tbl_inst->cur_size) { if (table_elem_idx >= tbl_inst->cur_size) {
aot_set_exception_with_id(module_inst, EXCE_UNDEFINED_ELEMENT); aot_set_exception_with_id(module_inst, EXCE_UNDEFINED_ELEMENT);
return false; goto fail;
} }
func_idx = tbl_inst->elems[table_elem_idx]; func_idx = tbl_inst->elems[table_elem_idx];
if (func_idx == NULL_REF) { if (func_idx == NULL_REF) {
aot_set_exception_with_id(module_inst, EXCE_UNINITIALIZED_ELEMENT); aot_set_exception_with_id(module_inst, EXCE_UNINITIALIZED_ELEMENT);
return false; goto fail;
} }
func_type_idx = func_type_indexes[func_idx]; func_type_idx = func_type_indexes[func_idx];
@ -1843,7 +1855,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
"failed to call unlinked import function (%s, %s)", "failed to call unlinked import function (%s, %s)",
import_func->module_name, import_func->func_name); import_func->module_name, import_func->func_name);
aot_set_exception(module_inst, buf); aot_set_exception(module_inst, buf);
return false; goto fail;
} }
if (func_idx < aot_module->import_func_count) { if (func_idx < aot_module->import_func_count) {
@ -1852,9 +1864,13 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
signature = import_func->signature; signature = import_func->signature;
if (import_func->call_conv_raw) { if (import_func->call_conv_raw) {
attachment = import_func->attachment; attachment = import_func->attachment;
return wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type, ret = wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type,
signature, attachment, argv, signature, attachment, argv,
argc, argv); argc, argv);
if (!ret)
goto fail;
return true;
} }
} }
@ -1878,7 +1894,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
&& !(argv1 = runtime_malloc(size, module_inst->cur_exception, && !(argv1 = runtime_malloc(size, module_inst->cur_exception,
sizeof(module_inst->cur_exception)))) { sizeof(module_inst->cur_exception)))) {
aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY); aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY);
return false; goto fail;
} }
/* Copy original arguments */ /* Copy original arguments */
@ -1897,12 +1913,10 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
ret = invoke_native_internal(exec_env, func_ptr, func_type, signature, ret = invoke_native_internal(exec_env, func_ptr, func_type, signature,
attachment, argv1, argc, argv); attachment, argv1, argc, argv);
if (!ret || aot_get_exception(module_inst)) { if (!ret) {
if (argv1 != argv1_buf) if (argv1 != argv1_buf)
wasm_runtime_free(argv1); wasm_runtime_free(argv1);
if (clear_wasi_proc_exit_exception(module_inst)) goto fail;
return true;
return false;
} }
/* Get extra result values */ /* Get extra result values */
@ -1941,10 +1955,20 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
else { else {
ret = invoke_native_internal(exec_env, func_ptr, func_type, signature, ret = invoke_native_internal(exec_env, func_ptr, func_type, signature,
attachment, argv, argc, argv); attachment, argv, argc, argv);
if (clear_wasi_proc_exit_exception(module_inst)) if (!ret)
return true; goto fail;
return ret;
return true;
} }
fail:
if (clear_wasi_proc_exit_exception(module_inst))
return true;
#ifdef OS_ENABLE_HW_BOUND_CHECK
wasm_runtime_access_exce_check_guard_page();
#endif
return false;
} }
bool bool
@ -1952,8 +1976,17 @@ aot_check_app_addr_and_convert(AOTModuleInstance *module_inst, bool is_str,
uint32 app_buf_addr, uint32 app_buf_size, uint32 app_buf_addr, uint32 app_buf_size,
void **p_native_addr) void **p_native_addr)
{ {
return wasm_check_app_addr_and_convert(module_inst, is_str, app_buf_addr, bool ret;
app_buf_size, p_native_addr);
ret = wasm_check_app_addr_and_convert(module_inst, is_str, app_buf_addr,
app_buf_size, p_native_addr);
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!ret)
wasm_runtime_access_exce_check_guard_page();
#endif
return ret;
} }
void * void *

View File

@ -56,6 +56,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
#endif #endif
#endif #endif
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!(exec_env->exce_check_guard_page =
os_mmap(NULL, os_getpagesize(), MMAP_PROT_NONE, MMAP_MAP_NONE)))
goto fail5;
#endif
exec_env->module_inst = module_inst; exec_env->module_inst = module_inst;
exec_env->wasm_stack_size = stack_size; exec_env->wasm_stack_size = stack_size;
exec_env->wasm_stack.s.top_boundary = exec_env->wasm_stack.s.top_boundary =
@ -76,6 +82,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
return exec_env; return exec_env;
#ifdef OS_ENABLE_HW_BOUND_CHECK
fail5:
#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
wasm_cluster_destroy_exenv_status(exec_env->current_status);
#endif
#endif
#if WASM_ENABLE_THREAD_MGR != 0 #if WASM_ENABLE_THREAD_MGR != 0
#if WASM_ENABLE_DEBUG_INTERP != 0 #if WASM_ENABLE_DEBUG_INTERP != 0
fail4: fail4:
@ -96,6 +108,9 @@ fail1:
void void
wasm_exec_env_destroy_internal(WASMExecEnv *exec_env) wasm_exec_env_destroy_internal(WASMExecEnv *exec_env)
{ {
#ifdef OS_ENABLE_HW_BOUND_CHECK
os_munmap(exec_env->exce_check_guard_page, os_getpagesize());
#endif
#if WASM_ENABLE_THREAD_MGR != 0 #if WASM_ENABLE_THREAD_MGR != 0
os_mutex_destroy(&exec_env->wait_lock); os_mutex_destroy(&exec_env->wait_lock);
os_cond_destroy(&exec_env->wait_cond); os_cond_destroy(&exec_env->wait_cond);

View File

@ -137,6 +137,8 @@ typedef struct WASMExecEnv {
#ifdef OS_ENABLE_HW_BOUND_CHECK #ifdef OS_ENABLE_HW_BOUND_CHECK
WASMJmpBuf *jmpbuf_stack_top; WASMJmpBuf *jmpbuf_stack_top;
/* One guard page for the exception check */
uint8 *exce_check_guard_page;
#endif #endif
#if WASM_ENABLE_MEMORY_PROFILING != 0 #if WASM_ENABLE_MEMORY_PROFILING != 0
@ -199,7 +201,8 @@ wasm_exec_env_alloc_wasm_frame(WASMExecEnv *exec_env, unsigned size)
the outs area contains const cells, its size may be larger than current the outs area contains const cells, its size may be larger than current
frame size, we should check again before putting the function arguments frame size, we should check again before putting the function arguments
into the outs area. */ into the outs area. */
if (addr + size * 2 > exec_env->wasm_stack.s.top_boundary) { if (size * 2
> (uint32)(uintptr_t)(exec_env->wasm_stack.s.top_boundary - addr)) {
/* WASM stack overflow. */ /* WASM stack overflow. */
return NULL; return NULL;
} }

View File

@ -185,6 +185,12 @@ runtime_signal_handler(void *sig_addr)
os_longjmp(jmpbuf_node->jmpbuf, 1); os_longjmp(jmpbuf_node->jmpbuf, 1);
} }
#endif #endif
else if (exec_env_tls->exce_check_guard_page <= (uint8 *)sig_addr
&& (uint8 *)sig_addr
< exec_env_tls->exce_check_guard_page + page_size) {
bh_assert(wasm_get_exception(module_inst));
os_longjmp(jmpbuf_node->jmpbuf, 1);
}
} }
} }
#else #else
@ -1435,6 +1441,17 @@ wasm_runtime_get_user_data(WASMExecEnv *exec_env)
return exec_env->user_data; return exec_env->user_data;
} }
#ifdef OS_ENABLE_HW_BOUND_CHECK
void
wasm_runtime_access_exce_check_guard_page()
{
if (exec_env_tls && exec_env_tls->handle == os_self_thread()) {
uint32 page_size = os_getpagesize();
memset(exec_env_tls->exce_check_guard_page, 0, page_size);
}
}
#endif
WASMType * WASMType *
wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function,
uint32 module_type) uint32 module_type)

View File

@ -554,6 +554,12 @@ wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data);
WASM_RUNTIME_API_EXTERN void * WASM_RUNTIME_API_EXTERN void *
wasm_runtime_get_user_data(WASMExecEnv *exec_env); wasm_runtime_get_user_data(WASMExecEnv *exec_env);
#ifdef OS_ENABLE_HW_BOUND_CHECK
/* Access exception check guard page to trigger the signal handler */
void
wasm_runtime_access_exce_check_guard_page();
#endif
/* See wasm_export.h for description */ /* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN bool WASM_RUNTIME_API_EXTERN bool
wasm_runtime_call_wasm(WASMExecEnv *exec_env, wasm_runtime_call_wasm(WASMExecEnv *exec_env,

View File

@ -35,7 +35,14 @@ create_func_return_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
/* Create return IR */ /* Create return IR */
LLVMPositionBuilderAtEnd(comp_ctx->builder, LLVMPositionBuilderAtEnd(comp_ctx->builder,
func_ctx->func_return_block); func_ctx->func_return_block);
if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { if (!comp_ctx->enable_bound_check) {
if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ALREADY_THROWN,
false, NULL, NULL)) {
return false;
}
}
else if (!aot_build_zero_function_ret(comp_ctx, func_ctx,
aot_func_type)) {
return false; return false;
} }
} }
@ -494,7 +501,8 @@ check_app_addr_and_convert(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
} }
/* Check whether exception was thrown when executing the function */ /* Check whether exception was thrown when executing the function */
if (!check_call_return(comp_ctx, func_ctx, res)) { if (comp_ctx->enable_bound_check
&& !check_call_return(comp_ctx, func_ctx, res)) {
return false; return false;
} }
@ -707,7 +715,8 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail; goto fail;
/* Check whether there was exception thrown when executing /* Check whether there was exception thrown when executing
the function */ the function */
if (!check_call_return(comp_ctx, func_ctx, res)) if (comp_ctx->enable_bound_check
&& !check_call_return(comp_ctx, func_ctx, res))
goto fail; goto fail;
} }
else { /* call native func directly */ else { /* call native func directly */
@ -823,7 +832,7 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
/* Check whether there was exception thrown when executing /* Check whether there was exception thrown when executing
the function */ the function */
if (!tail_call && !recursive_call if (!tail_call && !recursive_call && comp_ctx->enable_bound_check
&& !check_exception_thrown(comp_ctx, func_ctx)) && !check_exception_thrown(comp_ctx, func_ctx))
goto fail; goto fail;
} }
@ -1395,7 +1404,8 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail; goto fail;
/* Check whether exception was thrown when executing the function */ /* Check whether exception was thrown when executing the function */
if (!check_call_return(comp_ctx, func_ctx, res)) if (comp_ctx->enable_bound_check
&& !check_call_return(comp_ctx, func_ctx, res))
goto fail; goto fail;
block_curr = LLVMGetInsertBlock(comp_ctx->builder); block_curr = LLVMGetInsertBlock(comp_ctx->builder);
@ -1454,7 +1464,8 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
} }
/* Check whether exception was thrown when executing the function */ /* Check whether exception was thrown when executing the function */
if (!check_exception_thrown(comp_ctx, func_ctx)) if (comp_ctx->enable_bound_check
&& !check_exception_thrown(comp_ctx, func_ctx))
goto fail; goto fail;
if (func_result_count > 0) { if (func_result_count > 0) {

View File

@ -344,11 +344,12 @@ aot_apply_llvm_new_pass_manager(AOTCompContext *comp_ctx, LLVMModuleRef module)
if (!disable_llvm_lto) { if (!disable_llvm_lto) {
/* Apply LTO for AOT mode */ /* Apply LTO for AOT mode */
#if LLVM_VERSION_MAJOR < 14 if (comp_ctx->comp_data->func_count >= 10)
MPM.addPass(PB.buildLTODefaultPipeline(OL, NULL)); /* Adds the pre-link optimizations if the func count
#else is large enough */
MPM.addPass(PB.buildLTOPreLinkDefaultPipeline(OL)); MPM.addPass(PB.buildLTOPreLinkDefaultPipeline(OL));
#endif else
MPM.addPass(PB.buildLTODefaultPipeline(OL, NULL));
} }
else { else {
MPM.addPass(PB.buildPerModuleDefaultPipeline(OL)); MPM.addPass(PB.buildPerModuleDefaultPipeline(OL));

View File

@ -2781,7 +2781,11 @@ wasm_interp_dump_call_stack(struct WASMExecEnv *exec_env, bool print, char *buf,
void void
jit_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id) jit_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id)
{ {
wasm_set_exception_with_id(module_inst, id); if (id != EXCE_ALREADY_THROWN)
wasm_set_exception_with_id(module_inst, id);
#ifdef OS_ENABLE_HW_BOUND_CHECK
wasm_runtime_access_exce_check_guard_page();
#endif
} }
bool bool
@ -2789,8 +2793,15 @@ jit_check_app_addr_and_convert(WASMModuleInstance *module_inst, bool is_str,
uint32 app_buf_addr, uint32 app_buf_size, uint32 app_buf_addr, uint32 app_buf_size,
void **p_native_addr) void **p_native_addr)
{ {
return wasm_check_app_addr_and_convert(module_inst, is_str, app_buf_addr, bool ret = wasm_check_app_addr_and_convert(
app_buf_size, p_native_addr); module_inst, is_str, app_buf_addr, app_buf_size, p_native_addr);
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!ret)
wasm_runtime_access_exce_check_guard_page();
#endif
return ret;
} }
#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ #endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \
|| WASM_ENABLE_WAMR_COMPILER != 0 */ || WASM_ENABLE_WAMR_COMPILER != 0 */
@ -2811,12 +2822,20 @@ bool
llvm_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, llvm_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx,
uint32 argc, uint32 *argv) uint32 argc, uint32 *argv)
{ {
bool ret;
#if WASM_ENABLE_JIT != 0 #if WASM_ENABLE_JIT != 0
if (Wasm_Module_AoT == exec_env->module_inst->module_type) { if (Wasm_Module_AoT == exec_env->module_inst->module_type) {
return aot_call_indirect(exec_env, tbl_idx, elem_idx, argc, argv); return aot_call_indirect(exec_env, tbl_idx, elem_idx, argc, argv);
} }
#endif #endif
return call_indirect(exec_env, tbl_idx, elem_idx, argc, argv, false, 0);
ret = call_indirect(exec_env, tbl_idx, elem_idx, argc, argv, false, 0);
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!ret)
wasm_runtime_access_exce_check_guard_page();
#endif
return ret;
} }
bool bool
@ -2833,6 +2852,7 @@ llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc,
const char *signature; const char *signature;
void *attachment; void *attachment;
char buf[96]; char buf[96];
bool ret = false;
#if WASM_ENABLE_JIT != 0 #if WASM_ENABLE_JIT != 0
if (Wasm_Module_AoT == exec_env->module_inst->module_type) { if (Wasm_Module_AoT == exec_env->module_inst->module_type) {
@ -2855,27 +2875,34 @@ llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc,
"failed to call unlinked import function (%s, %s)", "failed to call unlinked import function (%s, %s)",
import_func->module_name, import_func->field_name); import_func->module_name, import_func->field_name);
wasm_set_exception(module_inst, buf); wasm_set_exception(module_inst, buf);
return false; goto fail;
} }
attachment = import_func->attachment; attachment = import_func->attachment;
if (import_func->call_conv_wasm_c_api) { if (import_func->call_conv_wasm_c_api) {
return wasm_runtime_invoke_c_api_native( ret = wasm_runtime_invoke_c_api_native(
(WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc, (WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc,
argv, import_func->wasm_c_api_with_env, attachment); argv, import_func->wasm_c_api_with_env, attachment);
} }
else if (!import_func->call_conv_raw) { else if (!import_func->call_conv_raw) {
signature = import_func->signature; signature = import_func->signature;
return wasm_runtime_invoke_native(exec_env, func_ptr, func_type, ret =
signature, attachment, argv, argc, wasm_runtime_invoke_native(exec_env, func_ptr, func_type, signature,
argv); attachment, argv, argc, argv);
} }
else { else {
signature = import_func->signature; signature = import_func->signature;
return wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type, ret = wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type,
signature, attachment, argv, argc, signature, attachment, argv, argc,
argv); argv);
} }
fail:
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!ret)
wasm_runtime_access_exce_check_guard_page();
#endif
return ret;
} }
#if WASM_ENABLE_BULK_MEMORY != 0 #if WASM_ENABLE_BULK_MEMORY != 0