diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 572384dd88..e061aa9e10 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -312,6 +312,7 @@ else () 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) diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 0797a018b8..629d5ad657 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -14,6 +14,13 @@ extern "C" { #endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define _EXCEWARNING LOG_WARNING /* for exception handling misbehavior logging */ +#define _EXCEDEBUG LOG_VERBOSE /* for exception handling debugging */ +#define _EXCEVERBOSE LOG_VERBOSE /* more excessive tracing of tagbrowsing and stack pointers */ +#endif + /** Value Type */ #define VALUE_TYPE_I32 0x7F #define VALUE_TYPE_I64 0X7E @@ -65,6 +72,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 +84,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]; @@ -197,6 +221,13 @@ typedef struct WASMFunctionImport { bool call_conv_wasm_c_api; } WASMFunctionImport; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTagImport { + uint8 attribute; /* the type of the tag (numerical) */ + uint32 type; /* the type of the catch function (numerical)*/ +} WASMTagImport; +#endif + typedef struct WASMGlobalImport { char *module_name; char *field_name; @@ -223,6 +254,9 @@ typedef struct WASMImport { WASMFunctionImport function; WASMTableImport table; WASMMemoryImport memory; +#if WASM_ENABLE_TAGS != 0 + WASMTagImport tag; +#endif WASMGlobalImport global; struct { char *module_name; @@ -261,6 +295,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 */ @@ -290,6 +328,13 @@ 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) */ +}; +#endif + struct WASMGlobal { uint8 type; bool is_mutable; @@ -417,6 +462,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; @@ -430,18 +478,28 @@ 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; WASMImport *imports; WASMFunction **functions; WASMTable *tables; WASMMemory *memories; +#if WASM_ENABLE_TAGS != 0 + WASMTag *tags; +#endif WASMGlobal *globals; WASMExport *exports; WASMTableSeg *table_segments; @@ -625,6 +683,10 @@ 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; /* Execution environment, e.g. stack info */ diff --git a/core/iwasm/interpreter/wasm_interp.h b/core/iwasm/interpreter/wasm_interp.h index d3692ff21b..b3bf30f9b6 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_t 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 653ee5b790..f33d1d28e6 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -336,6 +336,19 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) frame_sp += 2; \ } while (0) +#if WASM_ENABLE_EXCE_HANDLING != 0 +/* in exception handling, label_type needs to be stored to lookup exception handlers */ +#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; \ + frame_csp->cell_num = cell_num; \ + frame_csp->begin_addr = frame_ip; \ + frame_csp->target_addr = _target_addr; \ + frame_csp->frame_sp = frame_sp - param_cell_num; \ + frame_csp++; \ + } while (0) +#else #define PUSH_CSP(_label_type, param_cell_num, cell_num, _target_addr) \ do { \ bh_assert(frame_csp < frame->csp_boundary); \ @@ -346,6 +359,7 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) frame_csp->frame_sp = frame_sp - param_cell_num; \ frame_csp++; \ } while (0) +#endif #define POP_I32() (--frame_sp, *(int32 *)frame_sp) @@ -1173,8 +1187,14 @@ 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; + _EXCEDEBUG("IN %s\n", __FUNCTION__); + + #if WASM_ENABLE_DEBUG_INTERP != 0 uint8 *frame_ip_orig = NULL; WASMDebugInstance *debug_instance = wasm_exec_env_get_instance(exec_env); @@ -1206,6 +1226,466 @@ 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); + + _EXCEVERBOSE("hit the rethrow, relative_depth is %d, frame->frame_sp %p\n", relative_depth, frame_sp); + + if (frame_csp - relative_depth < frame->csp_bottom) { + /* according to validation, this should never happen */ + wasm_set_exception(module, "WASM_OP_THROW found no frame with exception handler. return exception from fucntion bo be implemented"); + goto got_exception; + } + + /* go up the frame stack */ + WASMBranchBlock * tgtframe = (frame_csp-1)-relative_depth; + + _EXCEVERBOSE("WASM_OP_RETHROW: frame[-%d] is %p, label_type is %d, frame->frame_sp %p\n", + relative_depth+1, tgtframe, tgtframe->label_type, tgtframe->frame_sp); + + switch (tgtframe->label_type) { + case LABEL_TYPE_BLOCK: + case LABEL_TYPE_IF: + case LABEL_TYPE_LOOP: + case LABEL_TYPE_TRY: + /* according to validation, this should never happen */ + wasm_set_exception(module, "WASM_OP_THROW found no frame with exception handler. return exception from fucntion bo be implemented"); + goto got_exception; + break; + case LABEL_TYPE_CATCH: + case 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; + + /* ready to take of */ + + _EXCEDEBUG("**** WASM_OP_RETHROW: beam me up to THROW ***\n"); + } + goto find_a_catch_handler; + break; + } + + if (frame_csp - relative_depth < frame->csp_bottom) { + /* according to validation, this should never happen */ + wasm_set_exception(module, "WASM_OP_THROW found no frame with exception handler. return exception from fucntion bo be implemented"); + goto got_exception; + } + + } + + + HANDLE_OP(WASM_OP_THROW) + { + read_leb_int32(frame_ip, frame_ip_end, exception_tag_index); +/* landig pad for the rethrow ? */ +find_a_catch_handler: __attribute__((unused)) + do {} while(0); + + _EXCEVERBOSE("hit the throw, exception_tag_index is %d, frame_sp %p\n", exception_tag_index, frame_sp); + + /* browse through frame stack */ + uint32 relative_depth = 0; + do { + POP_CSP_CHECK_OVERFLOW(relative_depth-1); + WASMBranchBlock * tgtframe = frame_csp - relative_depth - 1; + + _EXCEVERBOSE("WASM_OP_THROW: frame[-%d] is %p, label_type is %d, frame->frame_sp %p\n", + relative_depth+1, tgtframe, tgtframe->label_type, tgtframe->frame_sp); + + switch (tgtframe->label_type) { + case LABEL_TYPE_BLOCK: + case LABEL_TYPE_IF: + case LABEL_TYPE_LOOP: + _EXCEVERBOSE("skip block, if or loop frame\n"); + /* skip */ + break; + case LABEL_TYPE_TRY: + _EXCEVERBOSE("found try frame\n"); + { + uint32 handler_number = 0; + uint8 ** handlers = (uint8**) tgtframe->frame_sp; + uint8 * handler = NULL; + while ( (handler = handlers[handler_number]) != NULL ) + { + uint8 lookup_opcode = *handler; + uint8 * target_addr = handler + 1; /* first instruction or leb-immediate behind the handler opcode */ + _EXCEVERBOSE("handler address: %p, opcode 0x%x\n", handler, lookup_opcode); + switch (lookup_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); + + _EXCEVERBOSE("WASM_OP_THROW: found CATCH with tag_index %d\n", lookup_index); + + if (exception_tag_index == lookup_index) { + _EXCEDEBUG("**** WASM_OP_THROW: found CATCHING HANDLER in FRAME[-%d] at HANDLER %d ****\n", + relative_depth+1, handler_number); + + /* unwind */ + frame_csp -= relative_depth; + /* set ip */ + frame_ip = target_addr; + /* save frame_sp (points to exception values) */ + uint32 * frame_sp_old = frame_sp; + /* drop handlers and values pushd in try block */ + frame_sp = (frame_csp-1)->frame_sp; + + /* convert it into catch frame, this frame cannot + * longer seen as a try frame, because the handlers are not longer there + */ + (frame_csp-1)->label_type = LABEL_TYPE_CATCH; + + _EXCEVERBOSE("old_sp is %p, set frame_sp to %p\n", frame_sp_old, frame_sp); + /* transfer exception values */ + uint8 tag_type_index = module->module->tags[exception_tag_index].type; + uint32 cell_num_to_copy = wasm_types[tag_type_index]->param_cell_num; + /* push exception_tag_index and exception values for rethrow */ + PUSH_I32(exception_tag_index); + if (cell_num_to_copy > 0) { + 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 */ + if (cell_num_to_copy > 0) { + word_copy(frame_sp, frame_sp_old - cell_num_to_copy, + cell_num_to_copy); + } + frame_sp += cell_num_to_copy; + _EXCEVERBOSE("frame_sp now %p\n", frame_sp); + + /* 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); + + _EXCEDEBUG("**** WASM_OP_THROW: found DELEGATE HANDLER in FRAME[-%d] at HANDLER %d DEPTH %d ****\n", + relative_depth+1, handler_number,lookup_depth); + + /* the tag index is already in the exception_tag_index */ + + /* unwind to try frame + * the relative depth may be 0 if the delegate is on a try, but maybe larger + * if there are additional blocks in the try block or + * if THAT DELEGATE here is found by another delegate or a rethrow */ + frame_csp -= relative_depth; + + /* save frame_sp (points to exception values) */ + uint32 * frame_sp_old = frame_sp; + /* drop handlers and values pushd in try block */ + frame_sp = (frame_csp-1)->frame_sp; + + /* convert the discovered try frame into catch frame, this frame cannot + * longer seen as a try frame, because the handlers are not longer there + */ + (frame_csp-1)->label_type = LABEL_TYPE_CATCH; + + /* leave the block (the delegate is technically not inside the frame) */ + frame_csp--; + + /* unwind to delegated frame */ + frame_csp -= lookup_depth; + + _EXCEVERBOSE("old_sp is %p, set frame_sp to %p\n", frame_sp_old, frame_sp); + /* transfer exception values */ + uint8 tag_type_index = module->module->tags[exception_tag_index].type; + uint32 cell_num_to_copy = wasm_types[tag_type_index]->param_cell_num; + /* push exception values for catch */ + if (cell_num_to_copy > 0) { + word_copy(frame_sp, frame_sp_old - cell_num_to_copy, + cell_num_to_copy); + } + frame_sp += cell_num_to_copy; + _EXCEVERBOSE("DELEGATE: frame_sp now %p, beaming up to find a handler\n", frame_sp); + + /* tag_index is already stored in exception_tag_index */ + goto find_a_catch_handler; + } + break; + case WASM_OP_CATCH_ALL: + /* no immediate */ + _EXCEDEBUG("**** WASM_OP_THROW: found CATCH_ALL HANDLER in FRAME[-%d] at HANDLER %d ****\n", + relative_depth+1, handler_number); + + /* unwind */ + frame_csp -= relative_depth; + /* set ip */ + frame_ip = target_addr; + /* save frame_sp (points to exception values) */ + uint32 * frame_sp_old = frame_sp; + /* drop handlers and values pushd in try block */ + frame_sp = (frame_csp-1)->frame_sp; + + /* convert it into catch_all frame, this frame cannot + * longer seen as a try frame, because the handlers are not longer there + */ + (frame_csp-1)->label_type = LABEL_TYPE_CATCH_ALL; + + _EXCEVERBOSE("old_sp is %p, set frame_sp to %p\n", frame_sp_old, frame_sp); + /* transfer exception values */ + uint8 tag_type_index = module->module->tags[exception_tag_index].type; + uint32 cell_num_to_copy = wasm_types[tag_type_index]->param_cell_num; + /* push exception_tag_index and exception values for rethrow */ + PUSH_I32(exception_tag_index); + if (cell_num_to_copy > 0) { + 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 */ + _EXCEVERBOSE("frame_sp now %p\n", frame_sp); + + /* advance to handler */ + HANDLE_OP_END(); + break; + 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_CATCH: + _EXCEVERBOSE("skip catch frame\n"); + break; + case LABEL_TYPE_CATCH_ALL: + _EXCEVERBOSE("skip catch_all frame\n"); + break; + + case LABEL_TYPE_FUNCTION: + _EXCEDEBUG("**** WASM_OP_THROW: found LABEL_TYPE_FUNCTION, delegate this exception to the caller ****\n"); + + /* unwind to function frame */ + frame_csp -= relative_depth; + + /* save frame_sp (points to exception values) */ + uint32 * frame_sp_old = frame_sp; + /* drop handlers and values pushd in try block */ + frame_sp = (frame_csp-1)->frame_sp; + + /* transfer exception values */ + uint8 tag_type_index = module->module->tags[exception_tag_index].type; + uint32 cell_num_to_copy = wasm_types[tag_type_index]->param_cell_num; + /* push exception values for catch + * The values are copied to the CALLER FRAME (prev_frame->sp) + * same behvior ad WASM_OP_RETURN + */ + _EXCEDEBUG("**** WASM_OP_THROW: found LABEL_TYPE_FUNCTION, transfer %d words to the caller frame****\n", cell_num_to_copy); + if (cell_num_to_copy > 0) { + word_copy(prev_frame->sp, frame_sp_old - cell_num_to_copy, + cell_num_to_copy); + } + prev_frame->sp += cell_num_to_copy; + + /* mark frame as raised exception */ + frame->exception_raised = true; + frame->tag_index = exception_tag_index; + + /* end of function, treat as WASM_OP_RETURN */ + goto return_func; + } + + 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; + _EXCEVERBOSE("hit a ext_try, type_index is 0x%x (%d)\n", type_index, type_index); + 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); + _EXCEVERBOSE("hit a op_try, value_type is 0x%x (%d)\n", value_type, value_type); + + handle_op_try: + _EXCEVERBOSE("try: block in = %d, block_out = %d\n", param_cell_num, cell_num); + + 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 * lookup_catchhandler = 0; + uint8 lookup_opcode = WASM_OP_NOP; + uint32 lookup_index = 0; + uint32 lookup_depth = 0; + do { + /* lookup the 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; + } + lookup_opcode = *end_addr; + switch (lookup_opcode) { + case WASM_OP_CATCH: + case WASM_OP_DELEGATE: + lookup_cursor = end_addr+2; + break; + case WASM_OP_CATCH_ALL: + lookup_cursor = end_addr+1; + break; + case WASM_OP_END: + break; + default: + /* something went wrong */ + wasm_set_exception(module, "find block address returned an unexpected opcode"); + goto got_exception; + } + } while (lookup_opcode != WASM_OP_END && lookup_opcode != WASM_OP_DELEGATE); // *end_addr == WASM_OP_CATCH || *end_addr == WASM_OP_CATCH_ALL); + + /* create the try label */ + PUSH_CSP(LABEL_TYPE_TRY, param_cell_num, cell_num, end_addr); + + _EXCEVERBOSE("WASM_OP_TRY: push csp: frame is %p frame_sp %p frame->frame_sp %p\n", frame_csp-1, frame_sp, (frame_csp-1)->frame_sp); + + + /* 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, &lookup_catchhandler)) + { + /* something went wrong */ + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + lookup_opcode = *lookup_catchhandler; + + switch (lookup_opcode) { + case WASM_OP_CATCH: + lookup_cursor = lookup_catchhandler+1; + read_leb_int32(lookup_cursor, 0, lookup_index); + _EXCEVERBOSE("try: WASM_OP_CATCH lookup_catchhandler %p, lookup_index %d, frame_sp %p\n", lookup_catchhandler, lookup_index, frame_sp); + PUSH_I64(lookup_catchhandler); + break; + case WASM_OP_CATCH_ALL: + lookup_cursor = lookup_catchhandler+1; + _EXCEVERBOSE("try: WASM_OP_CATCH_ALL lookup_catchhandler %p, frame_sp %p\n", lookup_catchhandler, frame_sp); + PUSH_I64(lookup_catchhandler); + break; + case WASM_OP_DELEGATE: + lookup_cursor = lookup_catchhandler+1; + read_leb_int32(lookup_cursor, 0, lookup_depth); + _EXCEVERBOSE("try: WASM_OP_DELEGATE lookup_catchhandler %p, lookup_depth %d, frame_sp %p\n", lookup_catchhandler, lookup_depth, frame_sp); + PUSH_I64(lookup_catchhandler); + break; + case WASM_OP_END: + _EXCEVERBOSE("try: WASM_OP_END lookup_catchhandler %p, lookup_depth %d, frame_sp %p\n", lookup_catchhandler, lookup_depth, frame_sp); + PUSH_I64(0); + break; + default: + /* something went wrong */ + wasm_set_exception(module, "find block address returned an unexpected opcode"); + goto got_exception; + } + + + /* continue to search .... */ + + /* ... search until the returned address is the END of the TRY block */ + } while (lookup_opcode != WASM_OP_END && lookup_opcode != WASM_OP_DELEGATE); // *end_addr == WASM_OP_CATCH || *end_addr == WASM_OP_CATCH_ALL); + + + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_CATCH) + { + _EXCEVERBOSE("hit a catch clause, leave the frame\n"); + /* leave the frame */ + POP_CSP_N(0); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_CATCH_ALL) + { + _EXCEVERBOSE("hit a catch_all clause, leave the frame\n"); + /* leave the frame */ + POP_CSP_N(0); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_DELEGATE) + { + wasm_set_exception(module, "exception handling implementtion pending"); + /* that "exception" is the trap */ + goto got_exception; + } +#else + 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_0x18) + HANDLE_OP(WASM_OP_UNUSED_0x19) + { + wasm_set_exception(module, "exception handling opcode not implemented"); + /* that "esception" is the trap */ + goto got_exception; + } + +#endif HANDLE_OP(EXT_OP_BLOCK) { read_leb_uint32(frame_ip, frame_ip_end, type_index); @@ -1524,20 +2004,6 @@ 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) - { - /* TODO */ - wasm_set_exception(module, "unsupported opcode"); - goto got_exception; - } -#endif - /* parametric instructions */ HANDLE_OP(WASM_OP_DROP) { @@ -3816,14 +4282,6 @@ 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 - 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) #endif HANDLE_OP(WASM_OP_UNUSED_0x14) HANDLE_OP(WASM_OP_UNUSED_0x15) @@ -3906,12 +4364,21 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, 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; + + _EXCEDEBUG("%s: stack allocation: eh_size: %d, all_cell: %d\n", __FUNCTION__, eh_size, all_cell_num); /* 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 */ @@ -3960,6 +4427,24 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, FREE_FRAME(exec_env, frame); wasm_exec_env_set_cur_frame(exec_env, prev_frame); +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* the caller raised an exception */ + if (frame->exception_raised) { + _EXCEDEBUG("**** return_func: reset raise flag ****\n"); + frame->exception_raised = 0; + if (!prev_frame->ip) { + /* issue a trap */ + wasm_set_exception(module, "uncaught wasm exception"); + goto got_exception; + } + /* rethrow thw exception into that frame */ + _EXCEDEBUG("**** return_func: detected a thrown exception %d ****\n", frame->tag_index); + exception_tag_index = frame->tag_index; + RECOVER_CONTEXT(prev_frame); + goto find_a_catch_handler; + } +#endif + if (!prev_frame->ip) /* Called from native. */ return; diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 032e8f7857..ae85203b8f 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -1,4 +1,4 @@ -/* + /* * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ @@ -1386,6 +1386,60 @@ 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, + const char *sub_module_name, const char *tag_name, + WASMTagImport *tag, char *error_buf, + uint32 error_buf_size) +{ + uint8 tag_attribute; + uint32 tag_type; + const uint8 *p = *p_buf, *p_end = buf_end; + + /* get the one byte attribute */ + CHECK_BUF(p, p_end, 1); + tag_attribute = read_uint8(p); + if(tag_attribute != 0) + { + set_error_buf(error_buf, error_buf_size, "unknown tag attribute" ); + goto fail; + } + + /* get type */ + read_leb_uint32(p, p_end, tag_type); + /* compare against module->types */ + if (tag_type >= parent_module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown tag type"); + goto fail; + } + + /* check, that the type of the referred tag returns void */ + WASMType* func_type = (WASMType *)parent_module->types[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; + } + + /* store to module tag declarations */ + tag->attribute = tag_attribute; + tag->type = 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, @@ -1598,6 +1652,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; @@ -1626,7 +1683,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) { @@ -1667,6 +1724,14 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } break; +#if WASM_ENABLE_TAGS != 0 + case IMPORT_KIND_TAG: /* import tags */ + module->import_tag_count++; + 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; @@ -1689,10 +1754,14 @@ 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(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_table_count + module->import_memory_count + module->import_tag_count; p = p_old; @@ -1719,7 +1788,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) { @@ -1755,6 +1824,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++; @@ -2266,6 +2347,18 @@ 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 @@ -2275,6 +2368,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"); @@ -2681,6 +2775,88 @@ 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) +{ + LOG_VERBOSE("In %s\n", __FUNCTION__); + const uint8 *p = buf, *p_end = buf_end; + size_t total_size = 0; + uint32 section_tag_count = 0; // number of tags defined in the section + uint8 tag_attribute; + uint32 tag_type; + + _EXCEVERBOSE("%s buf=%p buf_end=%p, len=%ld\n", __FUNCTION__, buf, buf_end, (long)buf_end-(long)buf); + + /* get tag count */ + read_leb_uint32(p, p_end, section_tag_count); + module->tag_count = module->import_tag_count + section_tag_count; // total tags (imported and section) + _EXCEVERBOSE("%s: import_tag_count = %d, section_tag_count = %d\n", __FUNCTION__, module->import_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 */ + for (uint32 tag_index = module->import_tag_count; tag_index < module->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; + } + _EXCEVERBOSE("ReE: %s: tag_index %d, tag_attribute %d, tag_type_index %d, tag_type %p\n", + __FUNCTION__, tag_index, tag_attribute, tag_type, module->types[tag_type]); + + /* get return type (must be 0) */ + _EXCEVERBOSE("ReE: %s: tag_index %d, result_count %d\n", + __FUNCTION__, tag_index, module->types[tag_type]->result_count); + + /* 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; + } + + // WASMType * tag_func_type = (WASMType *) module->types[func_type]; + // printf("ReE: entry %d tag_type %d has func_type %d with %d params and %d returns\n", + // i, tag_type, func_type, mod_func_type->param_count, mod_func_type->result_count); + + /* store to module tag declarations */ + module->tags[tag_index].attribute = tag_attribute; + module->tags[tag_index].type = tag_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: + _EXCEVERBOSE("%s: something failed\n", __FUNCTION__); + + 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, @@ -3471,6 +3647,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)) @@ -3940,6 +4124,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, @@ -4468,6 +4655,12 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, i = ((uintptr_t)start_addr) & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* do not use the cache */ + if (label_type == LABEL_TYPE_TRY) { + _EXCEVERBOSE("wasm_loader_find_block_addr looking up the next important opcode a try frame\n"); + } +#else for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) { if (block[j].start_addr == start_addr) { /* Cache hit */ @@ -4476,6 +4669,7 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, return true; } } +#endif /* Cache unhit */ block_stack[0].start_addr = start_addr; @@ -4490,6 +4684,67 @@ 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: + _EXCEVERBOSE("wasm_loader_find_block_addr: WASM_OP_TRY\n"); + 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: + _EXCEVERBOSE("wasm_loader_find_block_addr: EXT_OP_TRY\n"); + 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) { + _EXCEVERBOSE("wasm_loader_find_block_addr: WASM_OP_CATCH, address is %p off %ld\n", p, p - start_addr + 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) { + _EXCEVERBOSE("wasm_loader_find_block_addr: WASM_OP_CATCH_ALL, address is %p off %ld\n", p, p - start_addr + 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) { + _EXCEVERBOSE("wasm_loader_find_block_addr: WASM_OP_DELEGATE, address is %p off %ld\n", p, p - start_addr + 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: @@ -4530,6 +4785,11 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, if (label_type == LABEL_TYPE_IF) *p_else_addr = else_addr; *p_end_addr = (uint8 *)(p - 1); +#if WASM_ENABLE_EXCE_HANDLING != 0 + if (label_type == LABEL_TYPE_TRY) { + _EXCEVERBOSE("wasm_loader_find_block_addr: TRY-END, address is %p, off %ld\n",*p_end_addr,p - start_addr); + } +#endif block_stack[0].end_addr = (uint8 *)(p - 1); for (t = 0; t < sizeof(block_stack) / sizeof(BlockAddr); @@ -4612,17 +4872,6 @@ 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: @@ -5369,6 +5618,11 @@ 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 = @@ -7304,6 +7558,27 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, goto handle_op_block_and_loop; case WASM_OP_BLOCK: case WASM_OP_LOOP: +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + if (opcode==WASM_OP_TRY) + { + _EXCEVERBOSE("ReE: wasm_loader_prepare_bytecode WASM_OP_TRY\n"); + + /* + * 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 @@ -7355,7 +7630,6 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, POP_TYPE( wasm_type->types[wasm_type->param_count - i - 1]); } - PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK), block_type, p); @@ -7369,6 +7643,11 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, if (opcode == WASM_OP_BLOCK) { skip_label(); } +#if WASM_ENABLE_EXCE_HANDLING != 0 + else if (opcode == WASM_OP_TRY) { + skip_label(); + } +#endif else if (opcode == WASM_OP_LOOP) { skip_label(); if (BLOCK_HAS_PARAM(block_type)) { @@ -7436,7 +7715,226 @@ 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 __attribute__((unused)) = 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->tag_count ) { + set_error_buf(error_buf, error_buf_size, "unknown tag index"); + goto fail; + } + + // the index of the type stored in the tag declaration + uint8 tag_type_index = module->tags[tag_index].type; + + /* check validity of tag_type_index */ + if (tag_type_index >= module->type_count) { + set_error_buf(error_buf, error_buf_size, + "unknown tag type index"); + goto fail; + } + + /* check, that the type of the referred tag returns void */ + WASMType* func_type = (WASMType *)module->types[tag_type_index]; + if(func_type->result_count != 0) + { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + goto fail; + } + + _EXCEVERBOSE("ReE: wasm_loader_prepare_bytecode WASM_OP_THROW, current label is %d, tag index is %d, tag type index %d, tag type %p\n", + label_type, + tag_index, + tag_type_index, + module->types[tag_type_index]); + + /* throw is stack polymorphic */ + 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) + { + _EXCEVERBOSE("ReE: %s WASM_OP_RETHROW, unexpected target label: %d\n", + __FUNCTION__, + frame_csp_tmp->label_type + ); + + + // set_error_buf(error_buf, error_buf_size, + // "type mismatch: rethrow target must " + // "be of catch or catch_all type."); + + /* 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 __attribute__((unused)) = cur_block->label_type; + + _EXCEVERBOSE("ReE: %s WASM_OP_RETHROW, current label is %d, target label %d\n", + __FUNCTION__, + label_type, + frame_csp_tmp->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) + { + _EXCEVERBOSE("ReE: %s WASM_OP_DELEGATE, invalid target label: %d\n", + __FUNCTION__, + frame_csp_tmp->label_type + ); + set_error_buf(error_buf, error_buf_size, + "type mismatch: delegate target must " + "be of structured control block type."); + goto fail; + } + + BranchBlock * cur_block = loader_ctx->frame_csp - 1; + uint8 label_type __attribute__((unused)) = cur_block->label_type; + _EXCEVERBOSE("ReE: %s WASM_OP_DELEGATE, current label is %d, target label %d\n", + __FUNCTION__, + label_type, + frame_csp_tmp->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->tag_count ) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } + + // the index of the type stored in the tag declaration + uint8 tag_type_index = module->tags[tag_index].type; + + /* check validity of tag_type_index */ + if (tag_type_index >= module->type_count) { + set_error_buf(error_buf, error_buf_size, + "unknown tag type index"); + goto fail; + } + + /* check, that the type of the referred tag returns void */ + WASMType *func_type = module->types[tag_type_index]; + 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 = module->types[tag_type_index]; + + _EXCEVERBOSE("ReE: wasm_loader_prepare_bytecode WASM_OP_CATCH, current label_type is %d, tag_index is %d, tag_type_index is %d, block_type %p\n", + label_type, + tag_index, + tag_type_index, + new_block_type.u.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 */ + _EXCEVERBOSE("ReE: %s WASM_OP_CATCH_ALL, current label is %d\n", + __FUNCTION__, + cur_block->label_type + ); + + /* 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: { BlockType block_type = (loader_ctx->frame_csp - 1)->block_type; @@ -7904,19 +8402,6 @@ 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_v(error_buf, error_buf_size, "%s %02x", - "unsupported opcode", 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 287a570c7f..04f64433b0 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -20,12 +20,17 @@ typedef enum WASMOpcode { WASM_OP_LOOP = 0x03, /* loop */ WASM_OP_IF = 0x04, /* if */ WASM_OP_ELSE = 0x05, /* else */ - - WASM_OP_TRY = 0x06, /* try */ - WASM_OP_CATCH = 0x07, /* catch */ - WASM_OP_THROW = 0x08, /* throw */ - WASM_OP_RETHROW = 0x09, /* rethrow */ - +#if WASM_ENABLE_EXCE_HANDLING != 0 + 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 */ +#else + WASM_OP_UNUSED_0x06 = 0x06, + WASM_OP_UNUSED_0x07 = 0x07, + WASM_OP_UNUSED_0x08 = 0x08, + WASM_OP_UNUSED_0x09 = 0x09, +#endif WASM_OP_UNUSED_0x0a = 0x0a, WASM_OP_END = 0x0b, /* end */ @@ -42,9 +47,13 @@ typedef enum WASMOpcode { WASM_OP_UNUSED_0x15 = 0x15, WASM_OP_UNUSED_0x16 = 0x16, WASM_OP_UNUSED_0x17 = 0x17, - - WASM_OP_DELEGATE = 0x18, /* delegate */ - WASM_OP_CATCH_ALL = 0x19, /* catch_all */ +#if WASM_ENABLE_EXCE_HANDLING != 0 + WASM_OP_DELEGATE = 0x18, /* delegate block of the try catch*/ + WASM_OP_CATCH_ALL = 0x19, /* a catch_all handler in a try block */ +#else + WASM_OP_UNUSED_0x18 = 0x18, + WASM_OP_UNUSED_0x19 = 0x19, +#endif /* parametric instructions */ WASM_OP_DROP = 0x1a, /* drop */ @@ -273,7 +282,11 @@ typedef enum WASMOpcode { #if WASM_ENABLE_DEBUG_INTERP != 0 DEBUG_OP_BREAK = 0xd7, /* debug break point */ #endif - +#if WASM_ENABLE_EXCE_HANDLING != 0 + EXT_OP_TRY = 0xd8, /* try block with blocktype */ + /* throw, rethrow, delegate, catch and catch_all + * do not have a blocktype, as far as i see */ +#endif /* Post-MVP extend op prefix */ WASM_OP_MISC_PREFIX = 0xfc, WASM_OP_SIMD_PREFIX = 0xfd, @@ -690,6 +703,7 @@ typedef enum WASMAtomicEXTOpcode { */ #define WASM_INSTRUCTION_NUM 256 +#if WASM_ENABLE_EXCE_HANDLING != 0 #define DEFINE_GOTO_TABLE(type, _name) \ static type _name[WASM_INSTRUCTION_NUM] = { \ HANDLE_OPCODE(WASM_OP_UNREACHABLE), /* 0x00 */ \ @@ -907,11 +921,236 @@ 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), /* 0xd8 */ \ SET_GOTO_TABLE_ELEM(WASM_OP_MISC_PREFIX), /* 0xfc */ \ SET_GOTO_TABLE_ELEM(WASM_OP_ATOMIC_PREFIX), /* 0xfe */ \ DEF_DEBUG_BREAK_HANDLE() \ }; +#else +#define DEFINE_GOTO_TABLE(type, _name) \ + static type _name[WASM_INSTRUCTION_NUM] = { \ + HANDLE_OPCODE(WASM_OP_UNREACHABLE), /* 0x00 */ \ + HANDLE_OPCODE(WASM_OP_NOP), /* 0x01 */ \ + HANDLE_OPCODE(WASM_OP_BLOCK), /* 0x02 */ \ + 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_UNUSED_0x0a), /* 0x0a */ \ + HANDLE_OPCODE(WASM_OP_END), /* 0x0b */ \ + HANDLE_OPCODE(WASM_OP_BR), /* 0x0c */ \ + HANDLE_OPCODE(WASM_OP_BR_IF), /* 0x0d */ \ + HANDLE_OPCODE(WASM_OP_BR_TABLE), /* 0x0e */ \ + HANDLE_OPCODE(WASM_OP_RETURN), /* 0x0f */ \ + HANDLE_OPCODE(WASM_OP_CALL), /* 0x10 */ \ + HANDLE_OPCODE(WASM_OP_CALL_INDIRECT), /* 0x11 */ \ + HANDLE_OPCODE(WASM_OP_RETURN_CALL), /* 0x12 */ \ + HANDLE_OPCODE(WASM_OP_RETURN_CALL_INDIRECT), /* 0x13 */ \ + HANDLE_OPCODE(WASM_OP_UNUSED_0x14), /* 0x14 */ \ + 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_DROP), /* 0x1a */ \ + HANDLE_OPCODE(WASM_OP_SELECT), /* 0x1b */ \ + HANDLE_OPCODE(WASM_OP_SELECT_T), /* 0x1c */ \ + HANDLE_OPCODE(WASM_OP_GET_GLOBAL_64), /* 0x1d */ \ + HANDLE_OPCODE(WASM_OP_SET_GLOBAL_64), /* 0x1e */ \ + HANDLE_OPCODE(WASM_OP_SET_GLOBAL_AUX_STACK), /* 0x1f */ \ + HANDLE_OPCODE(WASM_OP_GET_LOCAL), /* 0x20 */ \ + HANDLE_OPCODE(WASM_OP_SET_LOCAL), /* 0x21 */ \ + HANDLE_OPCODE(WASM_OP_TEE_LOCAL), /* 0x22 */ \ + HANDLE_OPCODE(WASM_OP_GET_GLOBAL), /* 0x23 */ \ + HANDLE_OPCODE(WASM_OP_SET_GLOBAL), /* 0x24 */ \ + HANDLE_OPCODE(WASM_OP_TABLE_GET), /* 0x25 */ \ + HANDLE_OPCODE(WASM_OP_TABLE_SET), /* 0x26 */ \ + HANDLE_OPCODE(WASM_OP_UNUSED_0x27), /* 0x27 */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD), /* 0x28 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD), /* 0x29 */ \ + HANDLE_OPCODE(WASM_OP_F32_LOAD), /* 0x2a */ \ + HANDLE_OPCODE(WASM_OP_F64_LOAD), /* 0x2b */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD8_S), /* 0x2c */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD8_U), /* 0x2d */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD16_S), /* 0x2e */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD16_U), /* 0x2f */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD8_S), /* 0x30 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD8_U), /* 0x31 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD16_S), /* 0x32 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD16_U), /* 0x33 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD32_S), /* 0x34 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD32_U), /* 0x35 */ \ + HANDLE_OPCODE(WASM_OP_I32_STORE), /* 0x36 */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE), /* 0x37 */ \ + HANDLE_OPCODE(WASM_OP_F32_STORE), /* 0x38 */ \ + HANDLE_OPCODE(WASM_OP_F64_STORE), /* 0x39 */ \ + HANDLE_OPCODE(WASM_OP_I32_STORE8), /* 0x3a */ \ + HANDLE_OPCODE(WASM_OP_I32_STORE16), /* 0x3b */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE8), /* 0x3c */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE16), /* 0x3d */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE32), /* 0x3e */ \ + HANDLE_OPCODE(WASM_OP_MEMORY_SIZE), /* 0x3f */ \ + HANDLE_OPCODE(WASM_OP_MEMORY_GROW), /* 0x40 */ \ + HANDLE_OPCODE(WASM_OP_I32_CONST), /* 0x41 */ \ + HANDLE_OPCODE(WASM_OP_I64_CONST), /* 0x42 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONST), /* 0x43 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONST), /* 0x44 */ \ + HANDLE_OPCODE(WASM_OP_I32_EQZ), /* 0x45 */ \ + HANDLE_OPCODE(WASM_OP_I32_EQ), /* 0x46 */ \ + HANDLE_OPCODE(WASM_OP_I32_NE), /* 0x47 */ \ + HANDLE_OPCODE(WASM_OP_I32_LT_S), /* 0x48 */ \ + HANDLE_OPCODE(WASM_OP_I32_LT_U), /* 0x49 */ \ + HANDLE_OPCODE(WASM_OP_I32_GT_S), /* 0x4a */ \ + HANDLE_OPCODE(WASM_OP_I32_GT_U), /* 0x4b */ \ + HANDLE_OPCODE(WASM_OP_I32_LE_S), /* 0x4c */ \ + HANDLE_OPCODE(WASM_OP_I32_LE_U), /* 0x4d */ \ + HANDLE_OPCODE(WASM_OP_I32_GE_S), /* 0x4e */ \ + HANDLE_OPCODE(WASM_OP_I32_GE_U), /* 0x4f */ \ + HANDLE_OPCODE(WASM_OP_I64_EQZ), /* 0x50 */ \ + HANDLE_OPCODE(WASM_OP_I64_EQ), /* 0x51 */ \ + HANDLE_OPCODE(WASM_OP_I64_NE), /* 0x52 */ \ + HANDLE_OPCODE(WASM_OP_I64_LT_S), /* 0x53 */ \ + HANDLE_OPCODE(WASM_OP_I64_LT_U), /* 0x54 */ \ + HANDLE_OPCODE(WASM_OP_I64_GT_S), /* 0x55 */ \ + HANDLE_OPCODE(WASM_OP_I64_GT_U), /* 0x56 */ \ + HANDLE_OPCODE(WASM_OP_I64_LE_S), /* 0x57 */ \ + HANDLE_OPCODE(WASM_OP_I64_LE_U), /* 0x58 */ \ + HANDLE_OPCODE(WASM_OP_I64_GE_S), /* 0x59 */ \ + HANDLE_OPCODE(WASM_OP_I64_GE_U), /* 0x5a */ \ + HANDLE_OPCODE(WASM_OP_F32_EQ), /* 0x5b */ \ + HANDLE_OPCODE(WASM_OP_F32_NE), /* 0x5c */ \ + HANDLE_OPCODE(WASM_OP_F32_LT), /* 0x5d */ \ + HANDLE_OPCODE(WASM_OP_F32_GT), /* 0x5e */ \ + HANDLE_OPCODE(WASM_OP_F32_LE), /* 0x5f */ \ + HANDLE_OPCODE(WASM_OP_F32_GE), /* 0x60 */ \ + HANDLE_OPCODE(WASM_OP_F64_EQ), /* 0x61 */ \ + HANDLE_OPCODE(WASM_OP_F64_NE), /* 0x62 */ \ + HANDLE_OPCODE(WASM_OP_F64_LT), /* 0x63 */ \ + HANDLE_OPCODE(WASM_OP_F64_GT), /* 0x64 */ \ + HANDLE_OPCODE(WASM_OP_F64_LE), /* 0x65 */ \ + HANDLE_OPCODE(WASM_OP_F64_GE), /* 0x66 */ \ + HANDLE_OPCODE(WASM_OP_I32_CLZ), /* 0x67 */ \ + HANDLE_OPCODE(WASM_OP_I32_CTZ), /* 0x68 */ \ + HANDLE_OPCODE(WASM_OP_I32_POPCNT), /* 0x69 */ \ + HANDLE_OPCODE(WASM_OP_I32_ADD), /* 0x6a */ \ + HANDLE_OPCODE(WASM_OP_I32_SUB), /* 0x6b */ \ + HANDLE_OPCODE(WASM_OP_I32_MUL), /* 0x6c */ \ + HANDLE_OPCODE(WASM_OP_I32_DIV_S), /* 0x6d */ \ + HANDLE_OPCODE(WASM_OP_I32_DIV_U), /* 0x6e */ \ + HANDLE_OPCODE(WASM_OP_I32_REM_S), /* 0x6f */ \ + HANDLE_OPCODE(WASM_OP_I32_REM_U), /* 0x70 */ \ + HANDLE_OPCODE(WASM_OP_I32_AND), /* 0x71 */ \ + HANDLE_OPCODE(WASM_OP_I32_OR), /* 0x72 */ \ + HANDLE_OPCODE(WASM_OP_I32_XOR), /* 0x73 */ \ + HANDLE_OPCODE(WASM_OP_I32_SHL), /* 0x74 */ \ + HANDLE_OPCODE(WASM_OP_I32_SHR_S), /* 0x75 */ \ + HANDLE_OPCODE(WASM_OP_I32_SHR_U), /* 0x76 */ \ + HANDLE_OPCODE(WASM_OP_I32_ROTL), /* 0x77 */ \ + HANDLE_OPCODE(WASM_OP_I32_ROTR), /* 0x78 */ \ + HANDLE_OPCODE(WASM_OP_I64_CLZ), /* 0x79 */ \ + HANDLE_OPCODE(WASM_OP_I64_CTZ), /* 0x7a */ \ + HANDLE_OPCODE(WASM_OP_I64_POPCNT), /* 0x7b */ \ + HANDLE_OPCODE(WASM_OP_I64_ADD), /* 0x7c */ \ + HANDLE_OPCODE(WASM_OP_I64_SUB), /* 0x7d */ \ + HANDLE_OPCODE(WASM_OP_I64_MUL), /* 0x7e */ \ + HANDLE_OPCODE(WASM_OP_I64_DIV_S), /* 0x7f */ \ + HANDLE_OPCODE(WASM_OP_I64_DIV_U), /* 0x80 */ \ + HANDLE_OPCODE(WASM_OP_I64_REM_S), /* 0x81 */ \ + HANDLE_OPCODE(WASM_OP_I64_REM_U), /* 0x82 */ \ + HANDLE_OPCODE(WASM_OP_I64_AND), /* 0x83 */ \ + HANDLE_OPCODE(WASM_OP_I64_OR), /* 0x84 */ \ + HANDLE_OPCODE(WASM_OP_I64_XOR), /* 0x85 */ \ + HANDLE_OPCODE(WASM_OP_I64_SHL), /* 0x86 */ \ + HANDLE_OPCODE(WASM_OP_I64_SHR_S), /* 0x87 */ \ + HANDLE_OPCODE(WASM_OP_I64_SHR_U), /* 0x88 */ \ + HANDLE_OPCODE(WASM_OP_I64_ROTL), /* 0x89 */ \ + HANDLE_OPCODE(WASM_OP_I64_ROTR), /* 0x8a */ \ + HANDLE_OPCODE(WASM_OP_F32_ABS), /* 0x8b */ \ + HANDLE_OPCODE(WASM_OP_F32_NEG), /* 0x8c */ \ + HANDLE_OPCODE(WASM_OP_F32_CEIL), /* 0x8d */ \ + HANDLE_OPCODE(WASM_OP_F32_FLOOR), /* 0x8e */ \ + HANDLE_OPCODE(WASM_OP_F32_TRUNC), /* 0x8f */ \ + HANDLE_OPCODE(WASM_OP_F32_NEAREST), /* 0x90 */ \ + HANDLE_OPCODE(WASM_OP_F32_SQRT), /* 0x91 */ \ + HANDLE_OPCODE(WASM_OP_F32_ADD), /* 0x92 */ \ + HANDLE_OPCODE(WASM_OP_F32_SUB), /* 0x93 */ \ + HANDLE_OPCODE(WASM_OP_F32_MUL), /* 0x94 */ \ + HANDLE_OPCODE(WASM_OP_F32_DIV), /* 0x95 */ \ + HANDLE_OPCODE(WASM_OP_F32_MIN), /* 0x96 */ \ + HANDLE_OPCODE(WASM_OP_F32_MAX), /* 0x97 */ \ + HANDLE_OPCODE(WASM_OP_F32_COPYSIGN), /* 0x98 */ \ + HANDLE_OPCODE(WASM_OP_F64_ABS), /* 0x99 */ \ + HANDLE_OPCODE(WASM_OP_F64_NEG), /* 0x9a */ \ + HANDLE_OPCODE(WASM_OP_F64_CEIL), /* 0x9b */ \ + HANDLE_OPCODE(WASM_OP_F64_FLOOR), /* 0x9c */ \ + HANDLE_OPCODE(WASM_OP_F64_TRUNC), /* 0x9d */ \ + HANDLE_OPCODE(WASM_OP_F64_NEAREST), /* 0x9e */ \ + HANDLE_OPCODE(WASM_OP_F64_SQRT), /* 0x9f */ \ + HANDLE_OPCODE(WASM_OP_F64_ADD), /* 0xa0 */ \ + HANDLE_OPCODE(WASM_OP_F64_SUB), /* 0xa1 */ \ + HANDLE_OPCODE(WASM_OP_F64_MUL), /* 0xa2 */ \ + HANDLE_OPCODE(WASM_OP_F64_DIV), /* 0xa3 */ \ + HANDLE_OPCODE(WASM_OP_F64_MIN), /* 0xa4 */ \ + HANDLE_OPCODE(WASM_OP_F64_MAX), /* 0xa5 */ \ + HANDLE_OPCODE(WASM_OP_F64_COPYSIGN), /* 0xa6 */ \ + HANDLE_OPCODE(WASM_OP_I32_WRAP_I64), /* 0xa7 */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_S_F32), /* 0xa8 */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_U_F32), /* 0xa9 */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_S_F64), /* 0xaa */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_U_F64), /* 0xab */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND_S_I32), /* 0xac */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND_U_I32), /* 0xad */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_S_F32), /* 0xae */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_U_F32), /* 0xaf */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_S_F64), /* 0xb0 */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_U_F64), /* 0xb1 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_S_I32), /* 0xb2 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_U_I32), /* 0xb3 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_S_I64), /* 0xb4 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_U_I64), /* 0xb5 */ \ + HANDLE_OPCODE(WASM_OP_F32_DEMOTE_F64), /* 0xb6 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_S_I32), /* 0xb7 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_U_I32), /* 0xb8 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_S_I64), /* 0xb9 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_U_I64), /* 0xba */ \ + HANDLE_OPCODE(WASM_OP_F64_PROMOTE_F32), /* 0xbb */ \ + HANDLE_OPCODE(WASM_OP_I32_REINTERPRET_F32), /* 0xbc */ \ + HANDLE_OPCODE(WASM_OP_I64_REINTERPRET_F64), /* 0xbd */ \ + HANDLE_OPCODE(WASM_OP_F32_REINTERPRET_I32), /* 0xbe */ \ + HANDLE_OPCODE(WASM_OP_F64_REINTERPRET_I64), /* 0xbf */ \ + HANDLE_OPCODE(WASM_OP_I32_EXTEND8_S), /* 0xc0 */ \ + HANDLE_OPCODE(WASM_OP_I32_EXTEND16_S), /* 0xc1 */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND8_S), /* 0xc2 */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND16_S), /* 0xc3 */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND32_S), /* 0xc4 */ \ + HANDLE_OPCODE(WASM_OP_DROP_64), /* 0xc5 */ \ + HANDLE_OPCODE(WASM_OP_SELECT_64), /* 0xc6 */ \ + HANDLE_OPCODE(EXT_OP_GET_LOCAL_FAST), /* 0xc7 */ \ + HANDLE_OPCODE(EXT_OP_SET_LOCAL_FAST_I64), /* 0xc8 */ \ + HANDLE_OPCODE(EXT_OP_SET_LOCAL_FAST), /* 0xc9 */ \ + HANDLE_OPCODE(EXT_OP_TEE_LOCAL_FAST), /* 0xca */ \ + HANDLE_OPCODE(EXT_OP_TEE_LOCAL_FAST_I64), /* 0xcb */ \ + HANDLE_OPCODE(EXT_OP_COPY_STACK_TOP), /* 0xcc */ \ + HANDLE_OPCODE(EXT_OP_COPY_STACK_TOP_I64), /* 0xcd */ \ + HANDLE_OPCODE(EXT_OP_COPY_STACK_VALUES), /* 0xce */ \ + HANDLE_OPCODE(WASM_OP_IMPDEP), /* 0xcf */ \ + HANDLE_OPCODE(WASM_OP_REF_NULL), /* 0xd0 */ \ + HANDLE_OPCODE(WASM_OP_REF_IS_NULL), /* 0xd1 */ \ + HANDLE_OPCODE(WASM_OP_REF_FUNC), /* 0xd2 */ \ + HANDLE_OPCODE(EXT_OP_BLOCK), /* 0xd3 */ \ + HANDLE_OPCODE(EXT_OP_LOOP), /* 0xd4 */ \ + HANDLE_OPCODE(EXT_OP_IF), /* 0xd5 */ \ + HANDLE_OPCODE(EXT_OP_BR_TABLE_CACHE), /* 0xd6 */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_MISC_PREFIX), /* 0xfc */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_ATOMIC_PREFIX), /* 0xfe */ \ + DEF_DEBUG_BREAK_HANDLE() \ + }; +#endif + #ifdef __cplusplus } #endif diff --git a/tests/wamr-test-suites/spec-test-script/all.py b/tests/wamr-test-suites/spec-test-script/all.py index bd5b89ce42..fc190e232e 100644 --- a/tests/wamr-test-suites/spec-test-script/all.py +++ b/tests/wamr-test-suites/spec-test-script/all.py @@ -65,8 +65,16 @@ def ignore_the_case( simd_flag=False, gc_flag=False, xip_flag=False, + eh_flag=False, qemu_flag=False ): + # print(f"case_name {case_name}\n") + if eh_flag and case_name in [ "tag", "try_catch", "rethrow", "try_delegate" ]: + return False + else: + return True + + if case_name in ["comments", "inline-module", "names"]: return True @@ -102,7 +110,10 @@ def ignore_the_case( return False -def preflight_check(aot_flag): +def preflight_check(aot_flag, eh_flag): + if eh_flag: + SPEC_TEST_DIR="exception-handling/test/core"; + if not pathlib.Path(SPEC_TEST_DIR).resolve().exists(): print(f"Can not find {SPEC_TEST_DIR}") return False @@ -127,6 +138,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, @@ -147,6 +159,7 @@ def test_case( simd_flag, gc_flag, xip_flag, + eh_flag, qemu_flag ): return True @@ -185,6 +198,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") @@ -254,6 +270,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, @@ -262,6 +279,9 @@ def test_suite( qemu_firmware='', log='', ): + if eh_flag: + SPEC_TEST_DIR="exception-handling/test/core"; + suite_path = pathlib.Path(SPEC_TEST_DIR).resolve() if not suite_path.exists(): print(f"can not find spec test cases at {suite_path}") @@ -296,6 +316,7 @@ def test_suite( multi_thread_flag, simd_flag, xip_flag, + eh_flag, clean_up_flag, verbose_flag, gc_flag, @@ -333,6 +354,7 @@ def test_suite( multi_thread_flag, simd_flag, xip_flag, + eh_flag, clean_up_flag, verbose_flag, gc_flag, @@ -392,6 +414,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", @@ -464,7 +494,8 @@ def main(): options = parser.parse_args() print(options) - if not preflight_check(options.aot_flag): + + if not preflight_check(options.aot_flag, options.eh_flag): return False if not options.cases: @@ -483,6 +514,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, @@ -507,6 +539,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/runtest.py b/tests/wamr-test-suites/spec-test-script/runtest.py index a1e505bd04..c2c599b028 100755 --- a/tests/wamr-test-suites/spec-test-script/runtest.py +++ b/tests/wamr-test-suites/spec-test-script/runtest.py @@ -224,6 +224,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") @@ -692,6 +695,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) @@ -917,6 +927,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) @@ -954,6 +1000,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 ] @@ -1168,6 +1216,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 e263e5ab68..1d9890056d 100755 --- a/tests/wamr-test-suites/test_wamr.sh +++ b/tests/wamr-test-suites/test_wamr.sh @@ -14,7 +14,7 @@ function help() { echo "test_wamr.sh [options]" echo "-c clean previous test results, not start test" - echo "-s {suite_name} test only one suite (spec|wasi_certification)" + echo "-s {suite_name} test only one suite (spec|wasi_certification|exception)" 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|multi-tier-jit)" echo "-M enable multi module feature" @@ -22,6 +22,8 @@ function help() echo "-S enable SIMD feature" echo "-G enable GC feature" echo "-X enable XIP feature" + # added to support WASM_ENABLE_EXCE_HANDLING + 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" @@ -46,6 +48,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 @@ -57,7 +60,7 @@ ENABLE_QEMU=0 QEMU_FIRMWARE="" WASI_TESTSUITE_COMMIT="aca78d919355ae00af141e6741a439039615b257" -while getopts ":s:cabgvt:m:MCpSXxwPGQF:" opt +while getopts ":s:cabgvt:m:MCpSXexwPGQF:" opt do OPT_PARSED="TRUE" case $opt in @@ -132,6 +135,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" @@ -486,6 +493,88 @@ function spec_test() echo -e "\nFinish spec tests" | tee -a ${REPORT_DIR}/spec_test_report.txt } +function exception_test() +{ + echo "Now start exception tests" + touch ${REPORT_DIR}/exception_test_report.txt + + cd ${WORK_DIR} + 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 HEAD + + popd + echo $(pwd) + + if [ ${WABT_BINARY_RELEASE} == "YES" ]; then + echo "download a binary release and install" + local WAT2WASM=${WORK_DIR}/wabt/out/gcc/Release/wat2wasm + if [ ! -f ${WAT2WASM} ]; then + case ${PLATFORM} in + linux) + WABT_PLATFORM=ubuntu + ;; + darwin) + WABT_PLATFORM=macos + ;; + *) + echo "wabt platform for ${PLATFORM} in unknown" + exit 1 + ;; + esac + if [ ! -f /tmp/wabt-1.0.31-${WABT_PLATFORM}.tar.gz ]; then + wget \ + https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-${WABT_PLATFORM}.tar.gz \ + -P /tmp + fi + + cd /tmp \ + && tar zxf wabt-1.0.31-${WABT_PLATFORM}.tar.gz \ + && mkdir -p ${WORK_DIR}/wabt/out/gcc/Release/ \ + && install wabt-1.0.31/bin/wa* ${WORK_DIR}/wabt/out/gcc/Release/ \ + && cd - + fi + else + echo "download source code and compile and install" + if [ ! -d "wabt" ];then + echo "wabt not exist, clone it from github" + git clone --recursive https://github.com/WebAssembly/wabt + fi + echo "upate wabt" + cd wabt + git pull + git reset --hard origin/main + cd .. + make -C wabt gcc-release -j 4 + fi + + ln -sf ${WORK_DIR}/../spec-test-script/all.py . + ln -sf ${WORK_DIR}/../spec-test-script/runtest.py . + + local ARGS_FOR_SPEC_TEST="-e --no_clean_up " + + # set log directory + ARGS_FOR_SPEC_TEST+="--log ${REPORT_DIR}" + + cd ${WORK_DIR} + echo "python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/exception_test_report.txt" + python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/exception_test_report.txt + if [[ ${PIPESTATUS[0]} -ne 0 ]];then + echo -e "\nspec tests FAILED" | tee -a ${REPORT_DIR}/exception_test_report.txt + exit 1 + fi + cd - + + echo -e "\nFinish exception tests" | tee -a ${REPORT_DIR}/exception_test_report.txt +} + + function wasi_test() { echo "Now start wasi tests" @@ -754,6 +843,11 @@ 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" + EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_MULTI_MODULE=1" + fi echo "SANITIZER IS" $WAMR_BUILD_SANITIZER if [[ "$WAMR_BUILD_SANITIZER" == "ubsan" ]]; then