diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index cb0b7210b4..22783a5092 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -335,6 +335,11 @@ if (WAMR_BUILD_REF_TYPES EQUAL 1) else () message (" Reference types disabled") endif () +if (WAMR_BUILD_EXCE_HANDLING EQUAL 1) + add_definitions (-DWASM_ENABLE_EXCE_HANDLING=1) + add_definitions (-DWASM_ENABLE_TAGS=1) + message (" Exception Handling enabled") +endif () if (DEFINED WAMR_BH_VPRINTF) add_definitions (-DBH_VPRINTF=${WAMR_BH_VPRINTF}) endif () diff --git a/core/config.h b/core/config.h index ef322647fd..25c45107ee 100644 --- a/core/config.h +++ b/core/config.h @@ -457,6 +457,14 @@ #define WASM_ENABLE_REF_TYPES 0 #endif +#ifndef WASM_ENABLE_EXCE_HANDLING +#define WASM_ENABLE_EXCE_HANDLING 0 +#endif + +#ifndef WASM_ENABLE_TAGS +#define WASM_ENABLE_TAGS 0 +#endif + #ifndef WASM_ENABLE_SGX_IPFS #define WASM_ENABLE_SGX_IPFS 0 #endif diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 518e766425..60e1238ad3 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -65,6 +65,9 @@ extern "C" { #if WASM_ENABLE_BULK_MEMORY != 0 #define SECTION_TYPE_DATACOUNT 12 #endif +#if WASM_ENABLE_TAGS != 0 +#define SECTION_TYPE_TAG 13 +#endif #define SUB_SECTION_TYPE_MODULE 0 #define SUB_SECTION_TYPE_FUNC 1 @@ -74,20 +77,34 @@ extern "C" { #define IMPORT_KIND_TABLE 1 #define IMPORT_KIND_MEMORY 2 #define IMPORT_KIND_GLOBAL 3 +#if WASM_ENABLE_TAGS != 0 +#define IMPORT_KIND_TAG 4 +#endif #define EXPORT_KIND_FUNC 0 #define EXPORT_KIND_TABLE 1 #define EXPORT_KIND_MEMORY 2 #define EXPORT_KIND_GLOBAL 3 +#if WASM_ENABLE_TAGS != 0 +#define EXPORT_KIND_TAG 4 +#endif #define LABEL_TYPE_BLOCK 0 #define LABEL_TYPE_LOOP 1 #define LABEL_TYPE_IF 2 #define LABEL_TYPE_FUNCTION 3 +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define LABEL_TYPE_TRY 4 +#define LABEL_TYPE_CATCH 5 +#define LABEL_TYPE_CATCH_ALL 6 +#endif typedef struct WASMModule WASMModule; typedef struct WASMFunction WASMFunction; typedef struct WASMGlobal WASMGlobal; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTag WASMTag; +#endif typedef union V128 { int8 i8x16[16]; @@ -201,6 +218,24 @@ typedef struct WASMFunctionImport { bool call_conv_wasm_c_api; } WASMFunctionImport; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTagImport { + char *module_name; + char *field_name; + uint8 attribute; /* the type of the tag (numerical) */ + uint32 type; /* the type of the catch function (numerical)*/ + WASMType *tag_type; + void *tag_ptr_linked; + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* imported tag pointer after linked */ + WASMModule *import_module; + WASMTag *import_tag_linked; + uint32 import_tag_index_linked; +#endif +} WASMTagImport; +#endif + typedef struct WASMGlobalImport { char *module_name; char *field_name; @@ -227,6 +262,9 @@ typedef struct WASMImport { WASMFunctionImport function; WASMTableImport table; WASMMemoryImport memory; +#if WASM_ENABLE_TAGS != 0 + WASMTagImport tag; +#endif WASMGlobalImport global; struct { char *module_name; @@ -265,6 +303,10 @@ struct WASMFunction { uint32 const_cell_num; #endif +#if WASM_ENABLE_EXCE_HANDLING != 0 + uint32 exception_handler_count; +#endif + #if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ || WASM_ENABLE_WAMR_COMPILER != 0 /* Whether function has opcode memory.grow */ @@ -294,6 +336,14 @@ struct WASMFunction { #endif }; +#if WASM_ENABLE_TAGS != 0 +struct WASMTag { + uint8 attribute; /* the attribute property of the tag (expected to be 0) */ + uint32 type; /* the type of the tag (expected valid inden in type table) */ + WASMType *tag_type; +}; +#endif + struct WASMGlobal { uint8 type; bool is_mutable; @@ -420,6 +470,9 @@ struct WASMModule { uint32 function_count; uint32 table_count; uint32 memory_count; +#if WASM_ENABLE_TAGS != 0 + uint32 tag_count; +#endif uint32 global_count; uint32 export_count; uint32 table_seg_count; @@ -433,11 +486,17 @@ struct WASMModule { uint32 import_function_count; uint32 import_table_count; uint32 import_memory_count; +#if WASM_ENABLE_TAGS != 0 + uint32 import_tag_count; +#endif uint32 import_global_count; WASMImport *import_functions; WASMImport *import_tables; WASMImport *import_memories; +#if WASM_ENABLE_TAGS != 0 + WASMImport *import_tags; +#endif WASMImport *import_globals; WASMType **types; @@ -445,6 +504,9 @@ struct WASMModule { WASMFunction **functions; WASMTable *tables; WASMMemory *memories; +#if WASM_ENABLE_TAGS != 0 + WASMTag **tags; +#endif WASMGlobal *globals; WASMExport *exports; WASMTableSeg *table_segments; @@ -628,6 +690,11 @@ typedef struct WASMBranchBlock { uint8 *target_addr; uint32 *frame_sp; uint32 cell_num; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* in exception handling, label_type needs to be stored to lookup exception + * handlers */ + uint8 label_type; +#endif } WASMBranchBlock; /** diff --git a/core/iwasm/interpreter/wasm_interp.h b/core/iwasm/interpreter/wasm_interp.h index d3692ff21b..2b5a51ea8e 100644 --- a/core/iwasm/interpreter/wasm_interp.h +++ b/core/iwasm/interpreter/wasm_interp.h @@ -34,6 +34,14 @@ typedef struct WASMInterpFrame { uint64 time_started; #endif +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* set to true if the callee returns an exception rather than + * result values on the stack + */ + bool exception_raised; + uint32 tag_index; +#endif + #if WASM_ENABLE_FAST_INTERP != 0 /* Return offset of the first return value of current frame, the callee will put return values here continuously */ diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 7364a25fa3..10de23742f 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -338,10 +338,19 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) frame_sp += 2; \ } while (0) +/* in exception handling, label_type needs to be stored to lookup exception + * handlers */ + +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define SET_LABEL_TYPE(_label_type) frame_csp->label_type = _label_type +#else +#define SET_LABEL_TYPE(_label_type) (void)0 +#endif + #define PUSH_CSP(_label_type, param_cell_num, cell_num, _target_addr) \ do { \ bh_assert(frame_csp < frame->csp_boundary); \ - /* frame_csp->label_type = _label_type; */ \ + SET_LABEL_TYPE(_label_type); \ frame_csp->cell_num = cell_num; \ frame_csp->begin_addr = frame_ip; \ frame_csp->target_addr = _target_addr; \ @@ -392,6 +401,18 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) frame_sp -= n; \ } while (0) +#if WASM_ENABLE_EXCE_HANDLING != 0 +/* unwind the CSP to a given label and optionally modify the labeltype */ +#define UNWIND_CSP(N, T) \ + do { \ + /* unwind to function frame */ \ + frame_csp -= N; \ + /* drop handlers and values pushd in try block */ \ + frame_sp = (frame_csp - 1)->frame_sp; \ + (frame_csp - 1)->label_type = T ? T : (frame_csp - 1)->label_type; \ + } while (0) +#endif + #define SYNC_ALL_TO_FRAME() \ do { \ frame->sp = frame_sp; \ @@ -1188,6 +1209,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 local_idx, local_offset, global_idx; uint8 local_type, *global_addr; uint32 cache_index, type_index, param_cell_num, cell_num; +#if WASM_ENABLE_EXCE_HANDLING != 0 + int32_t exception_tag_index; +#endif uint8 value_type; #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 @@ -1230,6 +1254,379 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(WASM_OP_NOP) { HANDLE_OP_END(); } +#if WASM_ENABLE_EXCE_HANDLING != 0 + + HANDLE_OP(WASM_OP_RETHROW) + { + int32_t relative_depth; + read_leb_int32(frame_ip, frame_ip_end, relative_depth); + + /* No frame found with exception handler; validation should + * catch it */ + bh_assert(frame_csp >= frame->csp_bottom + relative_depth); + + /* go up the frame stack */ + WASMBranchBlock *tgtframe = (frame_csp - 1) - relative_depth; + + bh_assert(tgtframe->label_type == LABEL_TYPE_CATCH + || tgtframe->label_type == LABEL_TYPE_CATCH_ALL); + + /* tgtframe points to the frame containing a thrown + * exception */ + + uint32 *tgtframe_sp = tgtframe->frame_sp; + + /* frame sp of tgtframe points to catched exception */ + exception_tag_index = *((uint32 *)tgtframe_sp); + tgtframe_sp++; + + /* get tag type */ + uint8 tag_type_index = + module->module->tags[exception_tag_index]->type; + uint32 cell_num_to_copy = + wasm_types[tag_type_index]->param_cell_num; + + /* move exception parameters (if there are any) onto top + * of stack */ + if (cell_num_to_copy > 0) { + word_copy(frame_sp, tgtframe_sp - cell_num_to_copy, + cell_num_to_copy); + } + + frame_sp += cell_num_to_copy; + goto find_a_catch_handler; + } + + HANDLE_OP(WASM_OP_THROW) + { + read_leb_int32(frame_ip, frame_ip_end, exception_tag_index); + + /* landing pad for the rethrow ? */ + find_a_catch_handler: + { + WASMType *tag_type = NULL; + uint32 cell_num_to_copy = 0; + if (IS_INVALID_TAGINDEX(exception_tag_index)) { + /* + * invalid exception index, + * generated if a submodule throws an exception + * that has not been imported here + * + * This should result in a branch to the CATCH_ALL block, + * if there is one + */ + tag_type = NULL; + cell_num_to_copy = 0; + } + else { + if (module->e->tags[exception_tag_index].is_import_tag) { + tag_type = module->e->tags[exception_tag_index] + .u.tag_import->tag_type; + } + else { + tag_type = module->e->tags[exception_tag_index] + .u.tag->tag_type; + } + cell_num_to_copy = tag_type->param_cell_num; + } + + /* browse through frame stack */ + uint32 relative_depth = 0; + do { + POP_CSP_CHECK_OVERFLOW(relative_depth - 1); + WASMBranchBlock *tgtframe = frame_csp - relative_depth - 1; + + switch (tgtframe->label_type) { + case LABEL_TYPE_BLOCK: + case LABEL_TYPE_IF: + case LABEL_TYPE_LOOP: + case LABEL_TYPE_CATCH: + case LABEL_TYPE_CATCH_ALL: + /* + * skip that blocks in search + * BLOCK, IF and LOOP do not contain handlers and + * cannot catch exceptions. + * blocks marked as CATCH or + * CATCH_ALL did already caugth an exception and can + * only be a target for RETHROW, but cannot catch an + * exception again + */ + break; + case LABEL_TYPE_TRY: + { + uint32 handler_number = 0; + uint8 **handlers = (uint8 **)tgtframe->frame_sp; + uint8 *handler = NULL; + while ((handler = handlers[handler_number]) != 0) { + uint8 handler_opcode = *handler; + uint8 *target_addr = + handler + + 1; /* first instruction or leb-immediate + behind the handler opcode */ + switch (handler_opcode) { + case WASM_OP_CATCH: + { + int32 lookup_index = 0; + /* read the tag_index and advance + * target_addr to the first instruction + * in the block */ + read_leb_int32(target_addr, 0, + lookup_index); + + if (exception_tag_index + == lookup_index) { + /* set ip */ + frame_ip = target_addr; + /* save frame_sp (points to + * exception values) */ + uint32 *frame_sp_old = frame_sp; + + UNWIND_CSP(relative_depth, + LABEL_TYPE_CATCH); + + /* push exception_tag_index and + * exception values for rethrow */ + PUSH_I32(exception_tag_index); + word_copy(frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + /* push exception values for catch + */ + word_copy(frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + + /* advance to handler */ + HANDLE_OP_END(); + } + break; + } + case WASM_OP_DELEGATE: + { + int32 lookup_depth = 0; + /* read the depth */ + read_leb_int32(target_addr, 0, + lookup_depth); + + /* save frame_sp (points to exception + * values) */ + uint32 *frame_sp_old = frame_sp; + + UNWIND_CSP(relative_depth, + LABEL_TYPE_CATCH); + + /* leave the block (the delegate is + * technically not inside the frame) */ + frame_csp--; + + /* unwind to delegated frame */ + frame_csp -= lookup_depth; + + /* push exception values for catch */ + word_copy(frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + + /* tag_index is already stored in + * exception_tag_index */ + goto find_a_catch_handler; + } + case WASM_OP_CATCH_ALL: + { + /* no immediate */ + /* save frame_sp (points to exception + * values) */ + uint32 *frame_sp_old = frame_sp; + /* set ip */ + frame_ip = target_addr; + + UNWIND_CSP(relative_depth, + LABEL_TYPE_CATCH_ALL); + + /* push exception_tag_index and + * exception values for rethrow */ + PUSH_I32(exception_tag_index); + word_copy(frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + /* catch_all has no exception values */ + + /* advance to handler */ + HANDLE_OP_END(); + } + default: + wasm_set_exception( + module, "WASM_OP_THROW found " + "unexpected handler type"); + goto got_exception; + } + handler_number++; + } + /* exception not catched in this frame */ + break; + } + case LABEL_TYPE_FUNCTION: + { + /* save frame_sp (points to exception values) */ + uint32 *frame_sp_old = frame_sp; + + UNWIND_CSP(relative_depth, LABEL_TYPE_FUNCTION); + /* push exception values for catch + * The values are copied to the CALLER FRAME + * (prev_frame->sp) same behvior ad WASM_OP_RETURN + */ + word_copy(prev_frame->sp, + frame_sp_old - cell_num_to_copy, + cell_num_to_copy); + prev_frame->sp += cell_num_to_copy; + *((int32 *)(prev_frame->sp)) = exception_tag_index; + prev_frame->sp++; + + /* mark frame as raised exception */ + wasm_set_exception(module, + "uncaught wasm exception"); + + /* end of function, treat as WASM_OP_RETURN */ + goto return_func; + } + default: + wasm_set_exception( + module, + "unexpected or invalid label in THROW or " + "RETHROW when searching a catch handler"); + goto got_exception; + } + + relative_depth++; + + } while (1); + } + + /* something went wrong. normally, we should always find the + * func label. if not, stop the interpreter */ + wasm_set_exception( + module, "WASM_OP_THROW hit the bottom of the frame stack"); + goto got_exception; + } + + HANDLE_OP(EXT_OP_TRY) + { + /* read the blocktype */ + read_leb_uint32(frame_ip, frame_ip_end, type_index); + param_cell_num = wasm_types[type_index]->param_cell_num; + cell_num = wasm_types[type_index]->ret_cell_num; + goto handle_op_try; + } + + HANDLE_OP(WASM_OP_TRY) + { + value_type = *frame_ip++; + param_cell_num = 0; + cell_num = wasm_value_type_cell_num(value_type); + + handle_op_try: + + cache_index = ((uintptr_t)frame_ip) + & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + cache_items = exec_env->block_addr_cache[cache_index]; + if (cache_items[0].start_addr == frame_ip) { + cache_items[0].start_addr = 0; + } + if (cache_items[1].start_addr == frame_ip) { + cache_items[1].start_addr = 0; + } + + /* start at the first opcode following the try and its blocktype + */ + uint8 *lookup_cursor = frame_ip; + uint8 handler_opcode = WASM_OP_UNREACHABLE; + + /* target_addr filled in when END or DELEGATE is found */ + PUSH_CSP(LABEL_TYPE_TRY, param_cell_num, cell_num, 0); + + /* reset to begin of block */ + lookup_cursor = frame_ip; + do { + /* lookup the next CATCH, CATCH_ALL or END for this TRY */ + if (!wasm_loader_find_block_addr( + exec_env, (BlockAddr *)exec_env->block_addr_cache, + lookup_cursor, (uint8 *)-1, LABEL_TYPE_TRY, + &else_addr, &end_addr)) { + /* something went wrong */ + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + + /* place cursor for continuation past opcode */ + lookup_cursor = end_addr + 1; + + /* end_addr points to CATCH, CATCH_ALL, DELEGATE or END */ + handler_opcode = *end_addr; + switch (handler_opcode) { + case WASM_OP_CATCH: + skip_leb(lookup_cursor); /* skip tag_index */ + PUSH_I64(end_addr); + break; + case WASM_OP_CATCH_ALL: + PUSH_I64(end_addr); + break; + case WASM_OP_DELEGATE: + skip_leb(lookup_cursor); /* skip depth */ + PUSH_I64(end_addr); + /* patch target_addr */ + (frame_csp - 1)->target_addr = lookup_cursor; + break; + case WASM_OP_END: + PUSH_I64(0); + /* patch target_addr */ + (frame_csp - 1)->target_addr = end_addr; + break; + default: + /* something went wrong */ + wasm_set_exception(module, + "find block address returned an " + "unexpected opcode"); + goto got_exception; + } + /* ... search until the returned address is the END of the + * TRY block */ + } while (handler_opcode != WASM_OP_END + && handler_opcode != WASM_OP_DELEGATE); + /* handler setup on stack complete */ + + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_CATCH) + { + /* skip the tag_index */ + skip_leb(frame_ip); + /* leave the frame */ + POP_CSP_N(0); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_CATCH_ALL) + { + /* leave the frame */ + POP_CSP_N(0); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_DELEGATE) + { + /* skip the delegate depth */ + skip_leb(frame_ip); + /* leave the frame like WASM_OP_END */ + POP_CSP(); + HANDLE_OP_END(); + } +#endif HANDLE_OP(EXT_OP_BLOCK) { read_leb_uint32(frame_ip, frame_ip_end, type_index); @@ -3814,10 +4211,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #endif #if WASM_ENABLE_LABELS_AS_VALUES != 0 - HANDLE_OP(WASM_OP_UNUSED_0x06) - HANDLE_OP(WASM_OP_UNUSED_0x07) - HANDLE_OP(WASM_OP_UNUSED_0x08) - HANDLE_OP(WASM_OP_UNUSED_0x09) HANDLE_OP(WASM_OP_UNUSED_0x0a) #if WASM_ENABLE_TAIL_CALL == 0 HANDLE_OP(WASM_OP_RETURN_CALL) @@ -3834,6 +4227,15 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(WASM_OP_REF_IS_NULL) HANDLE_OP(WASM_OP_REF_FUNC) #endif +#if WASM_ENABLE_EXCE_HANDLING == 0 + HANDLE_OP(WASM_OP_TRY) + HANDLE_OP(WASM_OP_CATCH) + HANDLE_OP(WASM_OP_THROW) + HANDLE_OP(WASM_OP_RETHROW) + HANDLE_OP(WASM_OP_DELEGATE) + HANDLE_OP(WASM_OP_CATCH_ALL) + HANDLE_OP(EXT_OP_TRY) +#endif #if WASM_ENABLE_JIT != 0 && WASM_ENABLE_SIMD != 0 /* SIMD isn't supported by interpreter, but when JIT is enabled, `iwasm --interp ` may be run to @@ -3844,8 +4246,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(WASM_OP_UNUSED_0x15) HANDLE_OP(WASM_OP_UNUSED_0x16) HANDLE_OP(WASM_OP_UNUSED_0x17) - HANDLE_OP(WASM_OP_UNUSED_0x18) - HANDLE_OP(WASM_OP_UNUSED_0x19) HANDLE_OP(WASM_OP_UNUSED_0x27) /* Used by fast interpreter */ HANDLE_OP(EXT_OP_SET_LOCAL_FAST_I64) @@ -3896,6 +4296,50 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (cur_func->import_func_inst) { wasm_interp_call_func_import(module, exec_env, cur_func, prev_frame); +#if WASM_ENABLE_EXCE_HANDLING != 0 + char uncaught_exception[128] = { 0 }; + bool has_exception = + wasm_copy_exception(module, uncaught_exception); + if (has_exception + && strstr(uncaught_exception, "uncaught wasm exception")) { + /* fix framesp */ + UPDATE_ALL_FROM_FRAME(); + + uint32 import_exception; + /* initialize imported exception index to be invalid */ + SET_INVALID_TAGINDEX(import_exception); + + /* pull external exception */ + uint32 ext_exception = POP_I32(); + + /* external function came back with an exception or trap */ + /* lookup exception in import tags */ + WASMTagInstance *tag = module->e->tags; + for (uint32 t = 0; t < module->module->import_tag_count; + tag++, t++) { + + /* compare the module and the external index with the + * imort tag data */ + if ((cur_func->u.func_import->import_module + == tag->u.tag_import->import_module) + && (ext_exception + == tag->u.tag_import + ->import_tag_index_linked)) { + /* set the import_exception to the import tag */ + import_exception = t; + break; + } + } + /* + * excange the thrown exception (index valid in submodule) + * with the imported exception index (valid in this module) + * if the module did not import the exception, + * that results in a "INVALID_TAGINDEX", that triggers + * an CATCH_ALL block, if there is one. + */ + PUSH_I32(import_exception); + } +#endif } else #endif @@ -3916,19 +4360,57 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (memory) linear_mem_size = get_linear_mem_size(); #endif - if (wasm_copy_exception(module, NULL)) + if (wasm_copy_exception(module, NULL)) { +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* the caller raised an exception */ + char uncaught_exception[128] = { 0 }; + bool has_exception = + wasm_copy_exception(module, uncaught_exception); + + /* libc_builtin signaled a "exception thrown by stdc++" trap */ + if (has_exception + && strstr(uncaught_exception, + "exception thrown by stdc++")) { + wasm_set_exception(module, NULL); + + /* setup internal c++ rethrow */ + exception_tag_index = 0; + goto find_a_catch_handler; + } + + /* when throw hits the end of a function it signalles with a + * "uncaught wasm exception" trap */ + if (has_exception + && strstr(uncaught_exception, "uncaught wasm exception")) { + wasm_set_exception(module, NULL); + exception_tag_index = POP_I32(); + + /* rethrow the exception into that frame */ + goto find_a_catch_handler; + } +#endif goto got_exception; + } } else { WASMFunction *cur_wasm_func = cur_func->u.func; WASMType *func_type; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* account for exception handlers */ + /* bundle them here */ + uint32 eh_size = + cur_wasm_func->exception_handler_count * sizeof(uint8 *); + cur_wasm_func->max_stack_cell_num += eh_size; +#endif + func_type = cur_wasm_func->func_type; all_cell_num = cur_func->param_cell_num + cur_func->local_cell_num + cur_wasm_func->max_stack_cell_num + cur_wasm_func->max_block_num * (uint32)sizeof(WASMBranchBlock) / 4; + /* param_cell_num, local_cell_num, max_stack_cell_num and max_block_num are all no larger than UINT16_MAX (checked in loader), all_cell_num must be smaller than 1MB */ @@ -3977,11 +4459,19 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, FREE_FRAME(exec_env, frame); wasm_exec_env_set_cur_frame(exec_env, prev_frame); - if (!prev_frame->ip) + if (!prev_frame->ip) { /* Called from native. */ return; + } RECOVER_CONTEXT(prev_frame); +#if WASM_ENABLE_EXCE_HANDLING != 0 + if (wasm_get_exception(module)) { + wasm_set_exception(module, NULL); + exception_tag_index = POP_I32(); + goto find_a_catch_handler; + } +#endif HANDLE_OP_END(); } diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 521f547f08..335fefca04 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1437,6 +1437,20 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, goto call_func_from_interp; } +#if WASM_ENABLE_EXCE_HANDLING != 0 + HANDLE_OP(WASM_OP_TRY) + HANDLE_OP(WASM_OP_CATCH) + HANDLE_OP(WASM_OP_THROW) + HANDLE_OP(WASM_OP_RETHROW) + HANDLE_OP(WASM_OP_DELEGATE) + HANDLE_OP(WASM_OP_CATCH_ALL) + HANDLE_OP(EXT_OP_TRY) + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + /* parametric instructions */ HANDLE_OP(WASM_OP_SELECT) { @@ -3670,10 +3684,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #endif #if WASM_ENABLE_LABELS_AS_VALUES != 0 - HANDLE_OP(WASM_OP_UNUSED_0x06) - HANDLE_OP(WASM_OP_UNUSED_0x07) - HANDLE_OP(WASM_OP_UNUSED_0x08) - HANDLE_OP(WASM_OP_UNUSED_0x09) HANDLE_OP(WASM_OP_UNUSED_0x0a) #if WASM_ENABLE_TAIL_CALL == 0 HANDLE_OP(WASM_OP_RETURN_CALL) @@ -3688,6 +3698,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(WASM_OP_REF_NULL) HANDLE_OP(WASM_OP_REF_IS_NULL) HANDLE_OP(WASM_OP_REF_FUNC) +#endif +#if WASM_ENABLE_EXCE_HANDLING == 0 + /* if exception handling is disabled, these opcodes issue a trap */ + HANDLE_OP(WASM_OP_TRY) + HANDLE_OP(WASM_OP_CATCH) + HANDLE_OP(WASM_OP_THROW) + HANDLE_OP(WASM_OP_RETHROW) + HANDLE_OP(WASM_OP_DELEGATE) + HANDLE_OP(WASM_OP_CATCH_ALL) + HANDLE_OP(EXT_OP_TRY) #endif /* SELECT_T is converted to SELECT or SELECT_64 */ HANDLE_OP(WASM_OP_SELECT_T) @@ -3695,8 +3715,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP(WASM_OP_UNUSED_0x15) HANDLE_OP(WASM_OP_UNUSED_0x16) HANDLE_OP(WASM_OP_UNUSED_0x17) - HANDLE_OP(WASM_OP_UNUSED_0x18) - HANDLE_OP(WASM_OP_UNUSED_0x19) HANDLE_OP(WASM_OP_UNUSED_0x27) /* optimized op code */ HANDLE_OP(WASM_OP_F32_STORE) diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index db82ab4949..11417fa19a 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -707,7 +707,6 @@ wasm_loader_find_export(const WASMModule *module, const char *module_name, WASMExport *export = loader_find_export((WASMModuleCommon *)module, module_name, field_name, export_kind, error_buf, error_buf_size); - ; return export; } #endif @@ -898,6 +897,58 @@ wasm_loader_resolve_global(const char *module_name, const char *global_name, return global; } +#if WASM_ENABLE_TAGS != 0 +static WASMTag * +wasm_loader_resolve_tag(const char *module_name, const char *tag_name, + const WASMType *expected_tag_type, + uint32 *linked_tag_index, char *error_buf, + uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMTag *tag = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for tag %s", module_name, + tag_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + module = (WASMModule *)module_reg; + export = + wasm_loader_find_export(module, module_name, tag_name, EXPORT_KIND_TAG, + error_buf, error_buf_size); + if (!export) { + return NULL; + } + + /* resolve tag type and tag */ + if (export->index < module->import_tag_count) { + /* importing an imported tag from the submodule */ + tag = module->import_tags[export->index].u.tag.import_tag_linked; + } + else { + /* importing an section tag from the submodule */ + tag = module->tags[export->index - module->import_tag_count]; + } + + /* check function type */ + if (!wasm_type_equal(expected_tag_type, tag->tag_type)) { + LOG_DEBUG("%s.%s failed the type check", module_name, tag_name); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + if (linked_tag_index != NULL) { + *linked_tag_index = export->index; + } + + return tag; +} +#endif #endif /* end of WASM_ENABLE_MULTI_MODULE */ static bool @@ -1237,6 +1288,89 @@ load_memory_import(const uint8 **p_buf, const uint8 *buf_end, return false; } +#if WASM_ENABLE_TAGS != 0 +static bool +load_tag_import(const uint8 **p_buf, const uint8 *buf_end, + const WASMModule *parent_module, /* this module ! */ + const char *sub_module_name, const char *tag_name, + WASMTagImport *tag, /* structure to fill */ + char *error_buf, uint32 error_buf_size) +{ + /* attribute and type of the import statement */ + uint8 declare_tag_attribute; + uint32 declare_type_index; + const uint8 *p = *p_buf, *p_end = buf_end; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *sub_module = NULL; +#endif + + /* get the one byte attribute */ + CHECK_BUF(p, p_end, 1); + declare_tag_attribute = read_uint8(p); + if (declare_tag_attribute != 0) { + set_error_buf(error_buf, error_buf_size, "unknown tag attribute"); + goto fail; + } + + /* get type */ + read_leb_uint32(p, p_end, declare_type_index); + /* compare against module->types */ + if (declare_type_index >= parent_module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown tag type"); + goto fail; + } + + WASMType *declare_tag_type = parent_module->types[declare_type_index]; + + /* check, that the type of the declared tag returns void */ + if (declare_tag_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + + goto fail; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + sub_module = (WASMModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)parent_module, sub_module_name, error_buf, + error_buf_size); + if (!sub_module) { + return false; + } + /* wasm_loader_resolve_tag checks, that the imported tag + * and the declared tag have the same type + */ + uint32 linked_tag_index = 0; + WASMTag *linked_tag = wasm_loader_resolve_tag( + sub_module_name, tag_name, declare_tag_type, + &linked_tag_index /* out */, error_buf, error_buf_size); + if (linked_tag) { + tag->import_module = sub_module; + tag->import_tag_linked = linked_tag; + tag->import_tag_index_linked = linked_tag_index; + } + } +#endif + /* store to module tag declarations */ + tag->attribute = declare_tag_attribute; + tag->type = declare_type_index; + + tag->module_name = (char *)sub_module_name; + tag->field_name = (char *)tag_name; + tag->tag_type = declare_tag_type; + + *p_buf = p; + (void)parent_module; + + LOG_VERBOSE("Load tag import success\n"); + + return true; +fail: + return false; +} +#endif + static bool load_global_import(const uint8 **p_buf, const uint8 *buf_end, const WASMModule *parent_module, char *sub_module_name, @@ -1458,6 +1592,9 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMImport *import; WASMImport *import_functions = NULL, *import_tables = NULL; WASMImport *import_memories = NULL, *import_globals = NULL; +#if WASM_ENABLE_TAGS != 0 + WASMImport *import_tags = NULL; +#endif char *sub_module_name, *field_name; uint8 u8, kind; @@ -1486,7 +1623,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, p += name_len; CHECK_BUF(p, p_end, 1); - /* 0x00/0x01/0x02/0x03 */ + /* 0x00/0x01/0x02/0x03/0x04 */ kind = read_uint8(p); switch (kind) { @@ -1527,6 +1664,16 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } break; +#if WASM_ENABLE_TAGS != 0 + case IMPORT_KIND_TAG: /* import tags */ + /* it only counts the number of tags to import */ + module->import_tag_count++; + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); + read_leb_uint32(p, p_end, type_index); + break; +#endif + case IMPORT_KIND_GLOBAL: /* import global */ CHECK_BUF(p, p_end, 2); p += 2; @@ -1549,10 +1696,23 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, import_memories = module->import_memories = module->imports + module->import_function_count + module->import_table_count; + +#if WASM_ENABLE_TAGS != 0 + if (module->import_tag_count) + import_tags = module->import_tags = + module->imports + module->import_function_count + + module->import_table_count + module->import_memory_count; + if (module->import_global_count) + import_globals = module->import_globals = + module->imports + module->import_function_count + + module->import_table_count + module->import_memory_count + + module->import_tag_count; +#else if (module->import_global_count) import_globals = module->import_globals = module->imports + module->import_function_count + module->import_table_count + module->import_memory_count; +#endif p = p_old; @@ -1579,7 +1739,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, p += name_len; CHECK_BUF(p, p_end, 1); - /* 0x00/0x01/0x02/0x03 */ + /* 0x00/0x01/0x02/0x03/0x4 */ kind = read_uint8(p); switch (kind) { @@ -1615,6 +1775,18 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } break; +#if WASM_ENABLE_TAGS != 0 + case IMPORT_KIND_TAG: + bh_assert(import_tags); + import = import_tags++; + if (!load_tag_import(&p, p_end, module, sub_module_name, + field_name, &import->u.tag, error_buf, + error_buf_size)) { + return false; + } + break; +#endif + case IMPORT_KIND_GLOBAL: /* import global */ bh_assert(import_globals); import = import_globals++; @@ -2126,6 +2298,16 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return false; } break; +#if WASM_ENABLE_TAGS != 0 + /* export tag */ + case EXPORT_KIND_TAG: + if (index >= module->tag_count + module->import_tag_count) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + return false; + } + break; +#endif + /* global index */ case EXPORT_KIND_GLOBAL: if (index @@ -2135,6 +2317,7 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return false; } break; + default: set_error_buf(error_buf, error_buf_size, "invalid export kind"); @@ -2541,6 +2724,83 @@ load_datacount_section(const uint8 *buf, const uint8 *buf_end, } #endif +#if WASM_ENABLE_TAGS != 0 +static bool +load_tag_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_code, + const uint8 *buf_code_end, WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + (void)buf_code; + (void)buf_code_end; + + const uint8 *p = buf, *p_end = buf_end; + size_t total_size = 0; + /* number of tags defined in the section */ + uint32 section_tag_count = 0; + uint8 tag_attribute; + uint32 tag_type; + WASMTag *tag = NULL; + + /* get tag count */ + read_leb_uint32(p, p_end, section_tag_count); + module->tag_count = section_tag_count; + + if (section_tag_count) { + total_size = sizeof(WASMTag *) * module->tag_count; + if (!(module->tags = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + /* load each tag, imported tags precede the tags */ + uint32 tag_index; + for (tag_index = 0; tag_index < section_tag_count; tag_index++) { + + /* get the one byte attribute */ + CHECK_BUF(p, p_end, 1); + tag_attribute = read_uint8(p); + + /* get type */ + read_leb_uint32(p, p_end, tag_type); + /* compare against module->types */ + if (tag_type >= module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); + return false; + } + + /* get return type (must be 0) */ + /* check, that the type of the referred tag returns void */ + WASMType *func_type = (WASMType *)module->types[tag_type]; + if (func_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "non-empty tag result type"); + + goto fail; + } + + if (!(tag = module->tags[tag_index] = loader_malloc( + sizeof(WASMTag), error_buf, error_buf_size))) { + return false; + } + + /* store to module tag declarations */ + tag->attribute = tag_attribute; + tag->type = tag_type; + tag->tag_type = func_type; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load tag section success.\n"); + return true; +fail: + return false; +} +#endif + static bool load_code_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_func, const uint8 *buf_func_end, WASMModule *module, @@ -3336,6 +3596,14 @@ load_from_sections(WASMModule *module, WASMSection *sections, error_buf_size)) return false; break; +#if WASM_ENABLE_TAGS != 0 + case SECTION_TYPE_TAG: + /* load tag declaration section */ + if (!load_tag_section(buf, buf_end, buf_code, buf_code_end, + module, error_buf, error_buf_size)) + return false; + break; +#endif case SECTION_TYPE_GLOBAL: if (!load_global_section(buf, buf_end, module, error_buf, error_buf_size)) @@ -3805,6 +4073,9 @@ static uint8 section_ids[] = { SECTION_TYPE_FUNC, SECTION_TYPE_TABLE, SECTION_TYPE_MEMORY, +#if WASM_ENABLE_TAGS != 0 + SECTION_TYPE_TAG, +#endif SECTION_TYPE_GLOBAL, SECTION_TYPE_EXPORT, SECTION_TYPE_START, @@ -4210,6 +4481,16 @@ wasm_loader_unload(WASMModule *module) if (module->memories) wasm_runtime_free(module->memories); +#if WASM_ENABLE_TAGS != 0 + if (module->tags) { + for (i = 0; i < module->tag_count; i++) { + if (module->tags[i]) + wasm_runtime_free(module->tags[i]); + } + wasm_runtime_free(module->tags); + } +#endif + if (module->globals) wasm_runtime_free(module->globals); @@ -4350,6 +4631,64 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, case WASM_OP_NOP: break; +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + u8 = read_uint8(p); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + case EXT_OP_TRY: + skip_leb_uint32(p, p_end); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + case WASM_OP_CATCH: + if (block_nested_depth == 1) { + *p_end_addr = (uint8 *)(p - 1); + /* stop search and return the address of the catch block */ + return true; + } + break; + case WASM_OP_CATCH_ALL: + if (block_nested_depth == 1) { + *p_end_addr = (uint8 *)(p - 1); + /* stop search and return the address of the catch_all block + */ + return true; + } + break; + case WASM_OP_THROW: + /* skip tag_index */ + skip_leb(p); + break; + case WASM_OP_RETHROW: + /* skip depth */ + skip_leb(p); + break; + case WASM_OP_DELEGATE: + if (block_nested_depth == 1) { + *p_end_addr = (uint8 *)(p - 1); + return true; + } + else { + /* the DELEGATE opcode ends the tryblock, */ + block_nested_depth--; + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) + block_stack[block_nested_depth].end_addr = + (uint8 *)(p - 1); + } + break; +#endif + case WASM_OP_BLOCK: case WASM_OP_LOOP: case WASM_OP_IF: @@ -5221,6 +5560,10 @@ wasm_loader_ctx_init(WASMFunction *func, char *error_buf, uint32 error_buf_size) goto fail; loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; +#if WASM_ENABLE_EXCE_HANDLING != 0 + func->exception_handler_count = 0; +#endif + #if WASM_ENABLE_FAST_INTERP != 0 loader_ctx->frame_offset_size = sizeof(int16) * 32; if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = @@ -6832,11 +7175,26 @@ check_block_stack(WASMLoaderContext *loader_ctx, BranchBlock *block, return true; } - /* Check stack cell num equals return cell num */ if (available_stack_cell != return_cell_num) { +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* testspec: this error message format is expected by try_catch.wast */ + snprintf( + error_buf, error_buf_size, "type mismatch: %s requires [%s]%s[%s]", + block->label_type == LABEL_TYPE_TRY + || (block->label_type == LABEL_TYPE_CATCH + && return_cell_num > 0) + ? "instruction" + : "block", + return_cell_num > 0 ? type2str(return_types[0]) : "", + " but stack has ", + available_stack_cell > 0 ? type2str(*(loader_ctx->frame_ref - 1)) + : ""); + goto fail; +#else set_error_buf(error_buf, error_buf_size, "type mismatch: stack size does not match block type"); goto fail; +#endif } /* Check stack values match return types */ @@ -7185,6 +7543,24 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } case WASM_OP_BLOCK: case WASM_OP_LOOP: +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + if (opcode == WASM_OP_TRY) { + /* + * keep track of exception handlers to account for + * memory allocation + */ + func->exception_handler_count++; + + /* + * try is a block + * do nothing special, but execution continues to + * to handle_op_block_and_loop, + * and that be pushes the csp + */ + } + +#endif #if WASM_ENABLE_FAST_INTERP != 0 PRESERVE_LOCAL_FOR_BLOCK(); #endif @@ -7258,7 +7634,6 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, wasm_type->types[wasm_type->param_count - i - 1]); } } - PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK), block_type, p); @@ -7291,6 +7666,11 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, loader_ctx->p_code_compiled; } } +#if WASM_ENABLE_EXCE_HANDLING != 0 + else if (opcode == WASM_OP_TRY) { + skip_label(); + } +#endif else if (opcode == WASM_OP_IF) { BranchBlock *block = loader_ctx->frame_csp - 1; /* If block has parameters, we should make sure they are in @@ -7351,7 +7731,212 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, #endif break; } +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_THROW: + { + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + uint8 label_type = cur_block->label_type; + uint32 tag_index = 0; + read_leb_int32(p, p_end, tag_index); + + /* check validity of tag_index against module->tag_count */ + /* check tag index is within the tag index space */ + if (tag_index >= module->import_tag_count + module->tag_count) { + snprintf(error_buf, error_buf_size, "unknown tag %d", + tag_index); + goto fail; + } + /* the tag_type is stored in either the WASMTag (section tags) + * or WASMTagImport (import tag) */ + WASMType *tag_type = NULL; + if (tag_index < module->import_tag_count) { + tag_type = module->import_tags[tag_index].u.tag.tag_type; + } + else { + tag_type = + module->tags[tag_index - module->import_tag_count] + ->tag_type; + } + + if (tag_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + goto fail; + } + + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + + /* Check stack values match return types by comparing tag param + * types with stack cells */ + uint8 *frame_ref = loader_ctx->frame_ref; + for (int tti = (int32)tag_type->param_count - 1; tti >= 0; + tti--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, + tag_type->types[tti], error_buf, + error_buf_size)) { + snprintf(error_buf, error_buf_size, + "type mismatch: instruction requires [%s] but " + "stack has [%s]", + tag_type->param_count > 0 + ? type2str(tag_type->types[tti]) + : "", + available_stack_cell > 0 + ? type2str(*(loader_ctx->frame_ref - 1)) + : ""); + goto fail; + } + frame_ref -= wasm_value_type_cell_num(tag_type->types[tti]); + available_stack_cell -= + wasm_value_type_cell_num(tag_type->types[tti]); + } + + /* throw is stack polymorphic */ + (void)label_type; + RESET_STACK(); + + break; + } + case WASM_OP_RETHROW: + { + /* must be done before checking branch block */ + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + /* check the target catching block: LABEL_TYPE_CATCH */ + if (!(frame_csp_tmp = check_branch_block( + loader_ctx, &p, p_end, error_buf, error_buf_size))) + goto fail; + + if (frame_csp_tmp->label_type != LABEL_TYPE_CATCH + && frame_csp_tmp->label_type != LABEL_TYPE_CATCH_ALL) { + /* trap according to spectest (rethrow.wast) */ + set_error_buf(error_buf, error_buf_size, + "invalid rethrow label"); + goto fail; + } + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + uint8 label_type = cur_block->label_type; + (void)label_type; + /* rethrow is stack polymorphic */ + RESET_STACK(); + break; + } + case WASM_OP_DELEGATE: + { + /* check target block is valid */ + if (!(frame_csp_tmp = check_branch_block( + loader_ctx, &p, p_end, error_buf, error_buf_size))) + goto fail; + + /* valid types */ + if (LABEL_TYPE_TRY != frame_csp_tmp->label_type) { + snprintf(error_buf, error_buf_size, "unknown label"); + goto fail; + } + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + uint8 label_type = cur_block->label_type; + + (void)label_type; + /* DELEGATE ends the block */ + POP_CSP(); + break; + } + case WASM_OP_CATCH: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + uint8 label_type = cur_block->label_type; + uint32 tag_index = 0; + read_leb_int32(p, p_end, tag_index); + + /* check validity of tag_index against module->tag_count */ + /* check tag index is within the tag index space */ + if (tag_index >= module->import_tag_count + module->tag_count) { + LOG_VERBOSE("In %s, unknown tag at WASM_OP_CATCH\n", + __FUNCTION__); + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } + + /* the tag_type is stored in either the WASMTag (section tags) + * or WASMTagImport (import tag) */ + WASMType *func_type = NULL; + if (tag_index < module->import_tag_count) { + func_type = module->import_tags[tag_index].u.tag.tag_type; + } + else { + func_type = + module->tags[tag_index - module->import_tag_count] + ->tag_type; + } + + if (func_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + goto fail; + } + + /* check validity of current label (expect LABEL_TYPE_TRY or + * LABEL_TYPE_CATCH) */ + if ((LABEL_TYPE_CATCH != label_type) + && (LABEL_TYPE_TRY != label_type)) { + set_error_buf(error_buf, error_buf_size, + "Unexpected block sequence encountered."); + goto fail; + } + + BlockType new_block_type; + new_block_type.is_value_type = false; + new_block_type.u.type = func_type; + + /* + * replace frame_csp by LABEL_TYPE_CATCH + */ + cur_block->label_type = LABEL_TYPE_CATCH; + + /* RESET_STACK removes the values pushed in TRY or pervious + * CATCH Blocks */ + RESET_STACK(); + + /* push types on the stack according to catched type */ + if (BLOCK_HAS_PARAM(new_block_type)) { + for (i = 0; i < new_block_type.u.type->param_count; i++) + PUSH_TYPE(new_block_type.u.type->types[i]); + } + break; + } + case WASM_OP_CATCH_ALL: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + /* expecting a TRY or CATCH, anything else will be considered an + * error */ + if ((LABEL_TYPE_CATCH != cur_block->label_type) + && (LABEL_TYPE_TRY != cur_block->label_type)) { + set_error_buf(error_buf, error_buf_size, + "Unexpected block sequence encountered."); + goto fail; + } + + /* no immediates */ + /* replace frame_csp by LABEL_TYPE_CATCH_ALL */ + cur_block->label_type = LABEL_TYPE_CATCH_ALL; + + /* RESET_STACK removes the values pushed in TRY or pervious + * CATCH Blocks */ + RESET_STACK(); + + /* catch_all has no tagtype and therefore no parameters */ + break; + } +#endif case WASM_OP_ELSE: { BranchBlock *block = NULL; diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index 857212e179..3abf4a96e4 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -3259,6 +3259,17 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, u8 = read_uint8(p); /* 0x00 */ break; +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + case WASM_OP_CATCH: + case WASM_OP_THROW: + case WASM_OP_RETHROW: + case WASM_OP_DELEGATE: + case WASM_OP_CATCH_ALL: + /* TODO */ + return false; +#endif + case WASM_OP_DROP: case WASM_OP_SELECT: case WASM_OP_DROP_64: @@ -6173,6 +6184,18 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, break; } +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + case WASM_OP_CATCH: + case WASM_OP_THROW: + case WASM_OP_RETHROW: + case WASM_OP_DELEGATE: + case WASM_OP_CATCH_ALL: + /* TODO */ + set_error_buf(error_buf, error_buf_size, "unsupported opcode"); + goto fail; +#endif + case WASM_OP_DROP: { BranchBlock *cur_block = loader_ctx->frame_csp - 1; diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index 276d15c3dd..4f8a6a695e 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -20,11 +20,10 @@ typedef enum WASMOpcode { WASM_OP_LOOP = 0x03, /* loop */ WASM_OP_IF = 0x04, /* if */ WASM_OP_ELSE = 0x05, /* else */ - - WASM_OP_UNUSED_0x06 = 0x06, - WASM_OP_UNUSED_0x07 = 0x07, - WASM_OP_UNUSED_0x08 = 0x08, - WASM_OP_UNUSED_0x09 = 0x09, + WASM_OP_TRY = 0x06, /* try */ + WASM_OP_CATCH = 0x07, /* catch* */ + WASM_OP_THROW = 0x08, /* throw of a try catch */ + WASM_OP_RETHROW = 0x09, /* rethrow of a try catch */ WASM_OP_UNUSED_0x0a = 0x0a, WASM_OP_END = 0x0b, /* end */ @@ -41,8 +40,9 @@ typedef enum WASMOpcode { WASM_OP_UNUSED_0x15 = 0x15, WASM_OP_UNUSED_0x16 = 0x16, WASM_OP_UNUSED_0x17 = 0x17, - WASM_OP_UNUSED_0x18 = 0x18, - WASM_OP_UNUSED_0x19 = 0x19, + + WASM_OP_DELEGATE = 0x18, /* delegate block of the try catch*/ + WASM_OP_CATCH_ALL = 0x19, /* a catch_all handler in a try block */ /* parametric instructions */ WASM_OP_DROP = 0x1a, /* drop */ @@ -268,8 +268,10 @@ typedef enum WASMOpcode { EXT_OP_IF = 0xd5, /* if with blocktype */ EXT_OP_BR_TABLE_CACHE = 0xd6, /* br_table from cache */ + EXT_OP_TRY = 0xd7, /* try block with blocktype */ + #if WASM_ENABLE_DEBUG_INTERP != 0 - DEBUG_OP_BREAK = 0xd7, /* debug break point */ + DEBUG_OP_BREAK = 0xd8, /* debug break point */ #endif /* Post-MVP extend op prefix */ @@ -703,10 +705,10 @@ typedef enum WASMAtomicEXTOpcode { HANDLE_OPCODE(WASM_OP_LOOP), /* 0x03 */ \ HANDLE_OPCODE(WASM_OP_IF), /* 0x04 */ \ HANDLE_OPCODE(WASM_OP_ELSE), /* 0x05 */ \ - HANDLE_OPCODE(WASM_OP_UNUSED_0x06), /* 0x06 */ \ - HANDLE_OPCODE(WASM_OP_UNUSED_0x07), /* 0x07 */ \ - HANDLE_OPCODE(WASM_OP_UNUSED_0x08), /* 0x08 */ \ - HANDLE_OPCODE(WASM_OP_UNUSED_0x09), /* 0x09 */ \ + HANDLE_OPCODE(WASM_OP_TRY), /* 0x06 */ \ + HANDLE_OPCODE(WASM_OP_CATCH), /* 0x07 */ \ + HANDLE_OPCODE(WASM_OP_THROW), /* 0x08 */ \ + HANDLE_OPCODE(WASM_OP_RETHROW), /* 0x09 */ \ HANDLE_OPCODE(WASM_OP_UNUSED_0x0a), /* 0x0a */ \ HANDLE_OPCODE(WASM_OP_END), /* 0x0b */ \ HANDLE_OPCODE(WASM_OP_BR), /* 0x0c */ \ @@ -721,8 +723,8 @@ typedef enum WASMAtomicEXTOpcode { HANDLE_OPCODE(WASM_OP_UNUSED_0x15), /* 0x15 */ \ HANDLE_OPCODE(WASM_OP_UNUSED_0x16), /* 0x16 */ \ HANDLE_OPCODE(WASM_OP_UNUSED_0x17), /* 0x17 */ \ - HANDLE_OPCODE(WASM_OP_UNUSED_0x18), /* 0x18 */ \ - HANDLE_OPCODE(WASM_OP_UNUSED_0x19), /* 0x19 */ \ + HANDLE_OPCODE(WASM_OP_DELEGATE), /* 0x18 */ \ + HANDLE_OPCODE(WASM_OP_CATCH_ALL), /* 0x19 */ \ HANDLE_OPCODE(WASM_OP_DROP), /* 0x1a */ \ HANDLE_OPCODE(WASM_OP_SELECT), /* 0x1b */ \ HANDLE_OPCODE(WASM_OP_SELECT_T), /* 0x1c */ \ @@ -912,6 +914,7 @@ typedef enum WASMAtomicEXTOpcode { HANDLE_OPCODE(EXT_OP_LOOP), /* 0xd4 */ \ HANDLE_OPCODE(EXT_OP_IF), /* 0xd5 */ \ HANDLE_OPCODE(EXT_OP_BR_TABLE_CACHE), /* 0xd6 */ \ + HANDLE_OPCODE(EXT_OP_TRY), /* 0xd7 */ \ SET_GOTO_TABLE_ELEM(WASM_OP_MISC_PREFIX), /* 0xfc */ \ SET_GOTO_TABLE_SIMD_PREFIX_ELEM() /* 0xfd */ \ SET_GOTO_TABLE_ELEM(WASM_OP_ATOMIC_PREFIX), /* 0xfe */ \ diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 43844f2c6b..44ef3db030 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -732,6 +732,101 @@ functions_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, return functions; } +#if WASM_ENABLE_TAGS != 0 +/** + * Destroy tags instances. + */ +static void +tags_deinstantiate(WASMTagInstance *tags, void **import_tag_ptrs) +{ + if (tags) { + wasm_runtime_free(tags); + } + if (import_tag_ptrs) { + wasm_runtime_free(import_tag_ptrs); + } +} + +/** + * Instantiate tags in a module. + */ +static WASMTagInstance * +tags_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, + char *error_buf, uint32 error_buf_size) +{ + WASMImport *import; + uint32 i, tag_count = module->import_tag_count + module->tag_count; + uint64 total_size = sizeof(WASMTagInstance) * (uint64)tag_count; + WASMTagInstance *tags, *tag; + + if (!(tags = runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + total_size = sizeof(void *) * (uint64)module->import_tag_count; + if (total_size > 0 + && !(module_inst->e->import_tag_ptrs = + runtime_malloc(total_size, error_buf, error_buf_size))) { + wasm_runtime_free(tags); + return NULL; + } + + /* instantiate tags from import section */ + tag = tags; + import = module->import_tags; + for (i = 0; i < module->import_tag_count; i++, import++) { + tag->is_import_tag = true; + tag->u.tag_import = &import->u.tag; + tag->type = import->u.tag.type; + tag->attribute = import->u.tag.attribute; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->u.tag.import_module) { + if (!(tag->import_module_inst = get_sub_module_inst( + module_inst, import->u.tag.import_module))) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } + + if (!(tag->import_tag_inst = + wasm_lookup_tag(tag->import_module_inst, + import->u.tag.field_name, NULL))) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } + + /* Copy the imported tag to current instance */ + module_inst->e->import_tag_ptrs[i] = + tag->u.tag_import->import_tag_linked; + } +#endif + tag++; + } + + /* instantiate tags from tag section */ + for (i = 0; i < module->tag_count; i++) { + tag->is_import_tag = false; + tag->type = module->tags[i]->type; + tag->u.tag = module->tags[i]; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* tag->const_cell_num = function->u.func->const_cell_num; */ +#endif + tag++; + } + bh_assert((uint32)(tag - tags) == tag_count); + + return tags; + +#if WASM_ENABLE_MULTI_MODULE != 0 +fail: + tags_deinstantiate(tags, module_inst->e->import_tag_ptrs); + /* clean up */ + module_inst->e->import_tag_ptrs = NULL; + return NULL; +#endif +} +#endif + /** * Destroy global instances. */ @@ -931,6 +1026,52 @@ export_functions_instantiate(const WASMModule *module, return export_funcs; } +#if WASM_ENABLE_TAGS != 0 +/** + * Destroy export function instances. + */ +static void +export_tags_deinstantiate(WASMExportTagInstance *tags) +{ + if (tags) + wasm_runtime_free(tags); +} + +/** + * Instantiate export functions in a module. + */ +static WASMExportTagInstance * +export_tags_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_tag_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportTagInstance *export_tags, *export_tag; + WASMExport *export = module->exports; + uint32 i; + uint64 total_size = + sizeof(WASMExportTagInstance) * (uint64)export_tag_count; + + if (!(export_tag = export_tags = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + for (i = 0; i < module->export_count; i++, export ++) + if (export->kind == EXPORT_KIND_TAG) { + export_tag->name = export->name; + + bh_assert((uint32)(module_inst->export_tags)); + + export_tag->tag = &module_inst->e->tags[export->index]; + export_tag++; + } + + bh_assert((uint32)(export_tag - export_tags) == export_tag_count); + return export_tags; +} +#endif + #if WASM_ENABLE_MULTI_MODULE != 0 static void export_globals_deinstantiate(WASMExportGlobInstance *globals) @@ -1720,6 +1861,9 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, module_inst->table_count = module->import_table_count + module->table_count; module_inst->e->function_count = module->import_function_count + module->function_count; +#if WASM_ENABLE_TAGS != 0 + module_inst->e->tag_count = module->import_tag_count + module->tag_count; +#endif /* export */ module_inst->export_func_count = get_export_count(module, EXPORT_KIND_FUNC); @@ -1728,11 +1872,15 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, get_export_count(module, EXPORT_KIND_TABLE); module_inst->export_memory_count = get_export_count(module, EXPORT_KIND_MEMORY); +#if WASM_ENABLE_TAGS != 0 + module_inst->e->export_tag_count = + get_export_count(module, EXPORT_KIND_TAG); +#endif module_inst->export_global_count = get_export_count(module, EXPORT_KIND_GLOBAL); #endif - /* Instantiate memories/tables/functions */ + /* Instantiate memories/tables/functions/tags */ if ((module_inst->memory_count > 0 && !(module_inst->memories = memories_instantiate(module, module_inst, parent, heap_size, @@ -1748,6 +1896,15 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, && !(module_inst->export_functions = export_functions_instantiate( module, module_inst, module_inst->export_func_count, error_buf, error_buf_size))) +#if WASM_ENABLE_TAGS != 0 + || (module_inst->e->tag_count > 0 + && !(module_inst->e->tags = tags_instantiate( + module, module_inst, error_buf, error_buf_size))) + || (module_inst->e->export_tag_count > 0 + && !(module_inst->e->export_tags = export_tags_instantiate( + module, module_inst, module_inst->e->export_tag_count, + error_buf, error_buf_size))) +#endif #if WASM_ENABLE_MULTI_MODULE != 0 || (module_inst->export_global_count > 0 && !(module_inst->export_globals = export_globals_instantiate( @@ -1765,7 +1922,6 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, ) { goto fail; } - if (global_count > 0) { /* Initialize the global data */ global_data = module_inst->global_data; @@ -2188,8 +2344,16 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) tables_deinstantiate(module_inst); functions_deinstantiate(module_inst->e->functions, module_inst->e->function_count); +#if WASM_ENABLE_TAGS != 0 + tags_deinstantiate(module_inst->e->tags, module_inst->e->import_tag_ptrs); +#endif + globals_deinstantiate(module_inst->e->globals); export_functions_deinstantiate(module_inst->export_functions); +#if WASM_ENABLE_TAGS != 0 + export_tags_deinstantiate(module_inst->e->export_tags); +#endif + #if WASM_ENABLE_MULTI_MODULE != 0 export_globals_deinstantiate(module_inst->export_globals); #endif @@ -2270,6 +2434,21 @@ wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name) (void)module_inst->export_tables; return module_inst->tables[0]; } + +#if WASM_ENABLE_TAGS != 0 +WASMTagInstance * +wasm_lookup_tag(const WASMModuleInstance *module_inst, const char *name, + const char *signature) +{ + uint32 i; + for (i = 0; i < module_inst->e->export_tag_count; i++) + if (!strcmp(module_inst->e->export_tags[i].name, name)) + return module_inst->e->export_tags[i].tag; + (void)signature; + return NULL; +} +#endif + #endif #ifdef OS_ENABLE_HW_BOUND_CHECK diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 80ffde349d..b1224863ef 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -28,6 +28,9 @@ typedef struct WASMFunctionInstance WASMFunctionInstance; typedef struct WASMMemoryInstance WASMMemoryInstance; typedef struct WASMTableInstance WASMTableInstance; typedef struct WASMGlobalInstance WASMGlobalInstance; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTagInstance WASMTagInstance; +#endif /** * When LLVM JIT, WAMR compiler or AOT is enabled, we should ensure that @@ -191,6 +194,30 @@ struct WASMFunctionInstance { #endif }; +#if WASM_ENABLE_TAGS != 0 +struct WASMTagInstance { + bool is_import_tag; + /* tag attribute */ + uint8 attribute; + /* tag type index */ + uint32 type; + union { + WASMTagImport *tag_import; + WASMTag *tag; + } u; + +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *import_module_inst; + WASMTagInstance *import_tag_inst; +#endif +}; +#endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define INVALID_TAGINDEX ((uint32)0xFFFFFFFF) +#define SET_INVALID_TAGINDEX(tag) (tag = INVALID_TAGINDEX) +#define IS_INVALID_TAGINDEX(tag) ((tag & INVALID_TAGINDEX) == INVALID_TAGINDEX) +#endif typedef struct WASMExportFuncInstance { char *name; WASMFunctionInstance *function; @@ -211,6 +238,13 @@ typedef struct WASMExportMemInstance { WASMMemoryInstance *memory; } WASMExportMemInstance; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMExportTagInstance { + char *name; + WASMTagInstance *tag; +} WASMExportTagInstance; +#endif + /* wasm-c-api import function info */ typedef struct CApiFuncImport { /* host func pointer after linked */ @@ -263,6 +297,14 @@ typedef struct WASMModuleInstanceExtra { WASMTableInstance **table_insts_linked; #endif +#if WASM_ENABLE_TAGS != 0 + uint32 tag_count; + uint32 export_tag_count; + WASMTagInstance *tags; + WASMExportTagInstance *export_tags; + void **import_tag_ptrs; +#endif + #if WASM_ENABLE_MEMORY_PROFILING != 0 uint32 max_aux_stack_used; #endif @@ -461,6 +503,13 @@ wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name); WASMTableInstance * wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name); + +#if WASM_ENABLE_TAGS != 0 +WASMTagInstance * +wasm_lookup_tag(const WASMModuleInstance *module_inst, const char *name, + const char *signature); +#endif + #endif bool diff --git a/product-mini/platforms/nuttx/wamr.mk b/product-mini/platforms/nuttx/wamr.mk index 341034e512..748fb6a87b 100644 --- a/product-mini/platforms/nuttx/wamr.mk +++ b/product-mini/platforms/nuttx/wamr.mk @@ -359,6 +359,14 @@ else CFLAGS += -DWASM_ENABLE_REF_TYPES=0 endif +ifeq ($(CONFIG_INTERPRETERS_WAMR_ENABLE_EXCE_HANDLING),y) +CFLAGS += -DWASM_ENABLE_EXCE_HANDLING=1 +CFLAGS += -DWASM_ENABLE_TAGS=1 +else +CFLAGS += -DWASM_ENABLE_EXCE_HANDLING=0 +CFLAGS += -DWASM_ENABLE_TAGS=0 +endif + CFLAGS += -Wno-strict-prototypes -Wno-shadow -Wno-unused-variable CFLAGS += -Wno-int-conversion -Wno-implicit-function-declaration diff --git a/tests/wamr-test-suites/spec-test-script/all.py b/tests/wamr-test-suites/spec-test-script/all.py index 551a3176c6..7aa47cf413 100644 --- a/tests/wamr-test-suites/spec-test-script/all.py +++ b/tests/wamr-test-suites/spec-test-script/all.py @@ -47,6 +47,7 @@ def get_iwasm_cmd(platform: str) -> str: IWASM_SGX_CMD = "../../../product-mini/platforms/linux-sgx/enclave-sample/iwasm" IWASM_QEMU_CMD = "iwasm" SPEC_TEST_DIR = "spec/test/core" +EXCE_HANDLING_DIR = "exception-handling/test/core" WAST2WASM_CMD = exe_file_path("./wabt/out/gcc/Release/wat2wasm") SPEC_INTERPRETER_CMD = "spec/interpreter/wasm" WAMRC_CMD = "../../../wamr-compiler/build/wamrc" @@ -78,8 +79,10 @@ def ignore_the_case( simd_flag=False, gc_flag=False, xip_flag=False, + eh_flag=False, qemu_flag=False, ): + if case_name in ["comments", "inline-module", "names"]: return True @@ -126,7 +129,7 @@ def ignore_the_case( return False -def preflight_check(aot_flag): +def preflight_check(aot_flag, eh_flag): if not pathlib.Path(SPEC_TEST_DIR).resolve().exists(): print(f"Can not find {SPEC_TEST_DIR}") return False @@ -139,6 +142,10 @@ def preflight_check(aot_flag): print(f"Can not find {WAMRC_CMD}") return False + if eh_flag and not pathlib.Path(EXCE_HANDLING_DIR).resolve().exists(): + print(f"Can not find {EXCE_HANDLING_DIR}") + return False + return True @@ -151,6 +158,7 @@ def test_case( multi_thread_flag=False, simd_flag=False, xip_flag=False, + eh_flag=False, clean_up_flag=True, verbose_flag=True, gc_flag=False, @@ -195,6 +203,9 @@ def test_case( if xip_flag: CMD.append("--xip") + if eh_flag: + CMD.append("--eh") + if qemu_flag: CMD.append("--qemu") CMD.append("--qemu-firmware") @@ -268,6 +279,7 @@ def test_suite( multi_thread_flag=False, simd_flag=False, xip_flag=False, + eh_flag=False, clean_up_flag=True, verbose_flag=True, gc_flag=False, @@ -291,6 +303,15 @@ def test_suite( gc_case_list = sorted(suite_path.glob("gc/*.wast")) case_list.extend(gc_case_list) + if eh_flag: + eh_path = pathlib.Path(EXCE_HANDLING_DIR).resolve() + if not eh_path.exists(): + print(f"can not find spec test cases at {eh_path}") + return False + eh_case_list = sorted(eh_path.glob("*.wast")) + eh_case_list_include = [test for test in eh_case_list if test.stem in ["throw", "tag", "try_catch", "rethrow", "try_delegate"]] + case_list.extend(eh_case_list_include) + # ignore based on command line options filtered_case_list = [] for case_path in case_list: @@ -305,6 +326,7 @@ def test_suite( simd_flag, gc_flag, xip_flag, + eh_flag, qemu_flag, ): filtered_case_list.append(case_path) @@ -331,6 +353,7 @@ def test_suite( multi_thread_flag, simd_flag, xip_flag, + eh_flag, clean_up_flag, verbose_flag, gc_flag, @@ -369,6 +392,7 @@ def test_suite( multi_thread_flag, simd_flag, xip_flag, + eh_flag, clean_up_flag, verbose_flag, gc_flag, @@ -428,6 +452,14 @@ def main(): dest="xip_flag", help="Running with the XIP feature", ) + # added to support WASM_ENABLE_EXCE_HANDLING + parser.add_argument( + "-e", + action="store_true", + default=False, + dest="eh_flag", + help="Running with the exception-handling feature", + ) parser.add_argument( "-t", action="store_true", @@ -508,7 +540,7 @@ def main(): if options.target == "x86_32": options.target = "i386" - if not preflight_check(options.aot_flag): + if not preflight_check(options.aot_flag, options.eh_flag): return False if not options.cases: @@ -527,6 +559,7 @@ def main(): options.multi_thread_flag, options.simd_flag, options.xip_flag, + options.eh_flag, options.clean_up_flag, options.verbose_flag, options.gc_flag, @@ -552,6 +585,7 @@ def main(): options.multi_thread_flag, options.simd_flag, options.xip_flag, + options.eh_flag, options.clean_up_flag, options.verbose_flag, options.gc_flag, diff --git a/tests/wamr-test-suites/spec-test-script/exception_handling.patch b/tests/wamr-test-suites/spec-test-script/exception_handling.patch new file mode 100644 index 0000000000..0c9e8d40f5 --- /dev/null +++ b/tests/wamr-test-suites/spec-test-script/exception_handling.patch @@ -0,0 +1,20 @@ +diff --git a/test/core/try_catch.wast b/test/core/try_catch.wast +index 2a0e9ff6..f243489d 100644 +--- a/test/core/try_catch.wast ++++ b/test/core/try_catch.wast +@@ -203,7 +203,6 @@ + + (assert_return (invoke "catch-param-i32" (i32.const 5)) (i32.const 5)) + +-(assert_return (invoke "catch-imported") (i32.const 2)) + + (assert_return (invoke "catchless-try" (i32.const 0)) (i32.const 0)) + (assert_return (invoke "catchless-try" (i32.const 1)) (i32.const 1)) +@@ -231,7 +230,6 @@ + ) + ) + +-(assert_return (invoke "imported-mismatch") (i32.const 3)) + + (assert_malformed + (module quote "(module (func (catch_all)))") diff --git a/tests/wamr-test-suites/spec-test-script/runtest.py b/tests/wamr-test-suites/spec-test-script/runtest.py index dcc0c3361d..60d4607e9b 100755 --- a/tests/wamr-test-suites/spec-test-script/runtest.py +++ b/tests/wamr-test-suites/spec-test-script/runtest.py @@ -301,6 +301,9 @@ def assert_prompt(runner, prompts, timeout, is_need_execute_result): parser.add_argument('--xip', default=False, action='store_true', help="Enable XIP") +parser.add_argument('--eh', default=False, action='store_true', + help="Enable Exception Handling") + parser.add_argument('--multi-module', default=False, action='store_true', help="Enable Multi-thread") @@ -762,6 +765,13 @@ def test_assert(r, opts, mode, cmd, expected): if o.find(e) >= 0 or e.find(o) >= 0: return True + # wasm-exception thrown out of function call, not a trap + if mode=='wasmexception': + o = re.sub('^Exception: ', '', out) + e = re.sub('^Exception: ', '', expected) + if o.find(e) >= 0 or e.find(o) >= 0: + return True + ## 0x9:i32,-0x1:i32 -> ['0x9:i32', '-0x1:i32'] expected_list = re.split(',', expected) out_list = re.split(',', out) @@ -987,6 +997,42 @@ def test_assert_exhaustion(r,opts,form): expected = "Exception: %s\n" % m.group(3) test_assert(r, opts, "exhaustion", "%s %s" % (func, " ".join(args)), expected) + +# added to support WASM_ENABLE_EXCE_HANDLING +def test_assert_wasmexception(r,opts,form): + # params + + # ^ + # \(assert_exception\s+ + # \(invoke\s+"([^"]+)"\s+ + # (\(.*\))\s* + # () + # \)\s* + # \)\s* + # $ + m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*\)\s*$', form) + if not m: + # no params + + # ^ + # \(assert_exception\s+ + # \(invoke\s+"([^"]+)"\s* + # () + # \)\s* + # \)\s* + # $ + m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s*()\)\s*\)\s*$', form) + if not m: + raise Exception("unparsed assert_exception: '%s'" % form) + func = m.group(1) # function name + if m.group(2) == '': # arguments + args = [] + else: + args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])] + + expected = "Exception: uncaught wasm exception\n" + test_assert(r, opts, "wasmexception", "%s %s" % (func, " ".join(args)), expected) + def do_invoke(r, opts, form): # params m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form) @@ -1025,6 +1071,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts): # default arguments if opts.gc: cmd = [opts.wast2wasm, "-u", "-d", wast_tempfile, "-o", wasm_tempfile] + elif opts.eh: + cmd = [opts.wast2wasm, "--enable-thread", "--no-check", "--enable-exceptions", "--enable-tail-call", wast_tempfile, "-o", wasm_tempfile ] else: cmd = [opts.wast2wasm, "--enable-thread", "--no-check", wast_tempfile, "-o", wasm_tempfile ] @@ -1236,6 +1284,8 @@ def test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile, test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r) elif re.match("^\(assert_exhaustion\\b.*", form): test_assert_exhaustion(r, opts, form) + elif re.match("^\(assert_exception\\b.*", form): + test_assert_wasmexception(r, opts, form) elif re.match("^\(assert_unlinkable\\b.*", form): test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False) elif re.match("^\(assert_malformed\\b.*", form): diff --git a/tests/wamr-test-suites/test_wamr.sh b/tests/wamr-test-suites/test_wamr.sh index 663403233f..7f41fc43df 100755 --- a/tests/wamr-test-suites/test_wamr.sh +++ b/tests/wamr-test-suites/test_wamr.sh @@ -24,6 +24,7 @@ function help() echo "-S enable SIMD feature" echo "-G enable GC feature" echo "-X enable XIP feature" + echo "-e enable exception handling" echo "-x test SGX" echo "-w enable WASI threads" echo "-b use the wabt binary release package instead of compiling from the source code" @@ -50,6 +51,7 @@ COLLECT_CODE_COVERAGE=0 ENABLE_SIMD=0 ENABLE_GC=0 ENABLE_XIP=0 +ENABLE_EH=0 ENABLE_DEBUG_VERSION=0 ENABLE_GC_HEAP_VERIFY=0 #unit test case arrary @@ -70,7 +72,7 @@ WASI_TESTSUITE_COMMIT="ee807fc551978490bf1c277059aabfa1e589a6c2" TARGET_LIST=("AARCH64" "AARCH64_VFP" "ARMV7" "ARMV7_VFP" "THUMBV7" "THUMBV7_VFP" \ "RISCV32" "RISCV32_ILP32F" "RISCV32_ILP32D" "RISCV64" "RISCV64_LP64F" "RISCV64_LP64D") -while getopts ":s:cabgvt:m:MCpSXxwPGQF:j:T:" opt +while getopts ":s:cabgvt:m:MCpSXexwPGQF:j:T:" opt do OPT_PARSED="TRUE" case $opt in @@ -145,6 +147,10 @@ do echo "enable XIP feature" ENABLE_XIP=1 ;; + e) + echo "enable exception handling feature" + ENABLE_EH=1 + ;; x) echo "test SGX" SGX_OPT="--sgx" @@ -425,6 +431,26 @@ function spec_test() git apply ../../spec-test-script/thread_proposal_fix_atomic_case.patch fi + if [ ${ENABLE_EH} == 1 ]; then + echo "checkout exception-handling test cases" + popd + if [ ! -d "exception-handling" ];then + echo "exception-handling not exist, clone it from github" + git clone -b master --single-branch https://github.com/WebAssembly/exception-handling + fi + pushd exception-handling + + # restore and clean everything + git reset --hard 51c721661b671bb7dc4b3a3acb9e079b49778d36 + + if [[ ${ENABLE_MULTI_MODULE} == 0 ]]; then + git apply ../../spec-test-script/exception_handling.patch + fi + + popd + echo $(pwd) + fi + # update GC cases if [[ ${ENABLE_GC} == 1 ]]; then echo "checkout spec for GC proposal" @@ -463,6 +489,10 @@ function spec_test() fi fi + if [[ 1 == ${ENABLE_EH} ]]; then + ARGS_FOR_SPEC_TEST+="-e " + fi + # sgx only enable in interp mode and aot mode if [[ ${SGX_OPT} == "--sgx" ]];then if [[ $1 == 'classic-interp' || $1 == 'fast-interp' || $1 == 'aot' || $1 == 'fast-jit' ]]; then @@ -827,6 +857,10 @@ function trigger() EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_LIB_WASI_THREADS=1" fi + if [[ ${ENABLE_EH} == 1 ]]; then + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_EXCE_HANDLING=1" + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_TAIL_CALL=1" + fi echo "SANITIZER IS" $WAMR_BUILD_SANITIZER if [[ "$WAMR_BUILD_SANITIZER" == "ubsan" ]]; then