Jelajahi Sumber

Implement Exception Handling for classic interpreter (#3096)

This PR adds the initial support for WASM exception handling:
* Inside the classic interpreter only:
  * Initial handling of Tags
  * Initial handling of Exceptions based on W3C Exception Proposal
  * Import and Export of Exceptions and Tags
* Add `cmake -DWAMR_BUILD_EXCE_HANDLING=1/0` option to enable/disable
  the feature, and by default it is disabled
* Update the wamr-test-suites scripts to test the feature
* Additional CI/CD changes to validate the exception spec proposal cases

Refer to:
https://github.com/bytecodealliance/wasm-micro-runtime/issues/1884
https://github.com/bytecodealliance/wasm-micro-runtime/commit/587513f3c60f1707681af606a578a8c9f2dfb586
https://github.com/bytecodealliance/wasm-micro-runtime/commit/8bebfe9ad75695a2390d2db6ccbef8ba5350da34
https://github.com/bytecodealliance/wasm-micro-runtime/commit/59bccdfed82178c39ea4e14295ceb24289b3b857

Signed-off-by: Ricardo Aguilar <ricardoaguilar@siemens.com>
Co-authored-by: Chris Woods <chris.woods@siemens.com>
Co-authored-by: Rene Ermler <rene.ermler@siemens.com>
Co-authored-by: Trenner Thomas <trenner.thomas@siemens.com>
Wenyong Huang 1 tahun lalu
induk
melakukan
af318bac81

+ 5 - 0
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 ()

+ 8 - 0
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

+ 67 - 0
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;
 
 /**

+ 8 - 0
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 */

+ 499 - 9
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 <wasm_file>` 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();
     }
 

+ 24 - 6
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)

+ 590 - 5
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 @@ fail:
     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 @@ fail:
 }
 #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 @@ re_scan:
             }
             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 @@ re_scan:
                             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 @@ re_scan:
                             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 @@ re_scan:
 #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;

+ 23 - 0
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 @@ re_scan:
                 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;

+ 17 - 14
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 */ \

+ 181 - 2
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

+ 49 - 0
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

+ 8 - 0
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
 

+ 36 - 2
tests/wamr-test-suites/spec-test-script/all.py

@@ -47,6 +47,7 @@ IWASM_CMD = get_iwasm_cmd(PLATFORM_NAME)
 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,

+ 20 - 0
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)))")

+ 50 - 0
tests/wamr-test-suites/spec-test-script/runtest.py

@@ -301,6 +301,9 @@ parser.add_argument('--simd', default=False, action='store_true',
 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 @@ if __name__ == "__main__":
                 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):

+ 35 - 1
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