Przeglądaj źródła

Enhance GC subtyping checks (#3317)

Enhance the GC subtyping checks:
- Fix issues in the type equivalence check
- Enable the recursive type subtyping check
- Add a equivalence type flag in defined types of aot file, if there is an
  equivalence type before, just set it true and re-use the previous type
- Normalize the defined types for interpreter and AOT
- Enable spec test case type-equivalence.wast and type-subtyping.wast,
  and enable some commented cases
- Enable set WAMR_BUILD_SANITIZER from cmake variable
Wenyong Huang 1 rok temu
rodzic
commit
68bd30c6f9

+ 6 - 1
build-scripts/config_common.cmake

@@ -134,7 +134,9 @@ endif ()
 
 # Sanitizers
 
-set(WAMR_BUILD_SANITIZER $ENV{WAMR_BUILD_SANITIZER})
+if (NOT DEFINED WAMR_BUILD_SANITIZER)
+  set(WAMR_BUILD_SANITIZER $ENV{WAMR_BUILD_SANITIZER})
+endif ()
 
 if (NOT DEFINED WAMR_BUILD_SANITIZER)
   set(WAMR_BUILD_SANITIZER "")
@@ -554,3 +556,6 @@ else ()
   # Disable aot intrinsics for interp, fast-jit and llvm-jit
   add_definitions (-DWASM_ENABLE_AOT_INTRINSICS=0)
 endif ()
+if (NOT WAMR_BUILD_SANITIZER STREQUAL "")
+  message ("     Sanitizer ${WAMR_BUILD_SANITIZER} enabled")
+endif ()

+ 66 - 28
core/iwasm/aot/aot_loader.c

@@ -1525,30 +1525,42 @@ fail:
     return false;
 }
 
+static void
+destroy_type(AOTType *type)
+{
+#if WASM_ENABLE_GC != 0
+    if (type->ref_count > 1) {
+        /* The type is referenced by other types
+           of current aot module */
+        type->ref_count--;
+        return;
+    }
+
+    if (type->type_flag == WASM_TYPE_FUNC) {
+        AOTFuncType *func_type = (AOTFuncType *)type;
+        if (func_type->ref_type_maps != NULL) {
+            bh_assert(func_type->ref_type_map_count > 0);
+            wasm_runtime_free(func_type->ref_type_maps);
+        }
+    }
+    else if (type->type_flag == WASM_TYPE_STRUCT) {
+        AOTStructType *struct_type = (AOTStructType *)type;
+        if (struct_type->ref_type_maps != NULL) {
+            bh_assert(struct_type->ref_type_map_count > 0);
+            wasm_runtime_free(struct_type->ref_type_maps);
+        }
+    }
+#endif
+    wasm_runtime_free(type);
+}
+
 static void
 destroy_types(AOTType **types, uint32 count)
 {
     uint32 i;
     for (i = 0; i < count; i++) {
-
         if (types[i]) {
-#if WASM_ENABLE_GC != 0
-            if (types[i]->type_flag == WASM_TYPE_FUNC) {
-                AOTFuncType *func_type = (AOTFuncType *)types[i];
-                if (func_type->ref_type_maps != NULL) {
-                    bh_assert(func_type->ref_type_map_count > 0);
-                    wasm_runtime_free(func_type->ref_type_maps);
-                }
-            }
-            else if (types[i]->type_flag == WASM_TYPE_STRUCT) {
-                AOTStructType *struct_type = (AOTStructType *)types[i];
-                if (struct_type->ref_type_maps != NULL) {
-                    bh_assert(struct_type->ref_type_map_count > 0);
-                    wasm_runtime_free(struct_type->ref_type_maps);
-                }
-            }
-#endif
-            wasm_runtime_free(types[i]);
+            destroy_type(types[i]);
         }
     }
     wasm_runtime_free(types);
@@ -1556,14 +1568,17 @@ destroy_types(AOTType **types, uint32 count)
 
 #if WASM_ENABLE_GC != 0
 static void
-init_base_type(AOTType *base_type, uint16 type_flag, bool is_sub_final,
-               uint32 parent_type_idx, uint16 rec_count, uint16 rec_idx)
+init_base_type(AOTType *base_type, uint32 type_idx, uint16 type_flag,
+               bool is_sub_final, uint32 parent_type_idx, uint16 rec_count,
+               uint16 rec_idx)
 {
     base_type->type_flag = type_flag;
+    base_type->ref_count = 1;
     base_type->is_sub_final = is_sub_final;
     base_type->parent_type_idx = parent_type_idx;
     base_type->rec_count = rec_count;
     base_type->rec_idx = rec_idx;
+    base_type->rec_begin_type_idx = type_idx - rec_idx;
 }
 
 static bool
@@ -1576,7 +1591,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
     uint32 i, j;
     uint32 type_flag, param_cell_num, ret_cell_num;
     uint16 param_count, result_count, ref_type_map_count, rec_count, rec_idx;
-    bool is_sub_final;
+    bool is_equivalence_type, is_sub_final;
     uint32 parent_type_idx;
     WASMRefType ref_type;
 
@@ -1590,12 +1605,31 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
 
     /* Create each type */
     for (i = 0; i < module->type_count; i++) {
-
         buf = align_ptr(buf, 4);
 
         /* Read base type info */
         read_uint16(buf, buf_end, type_flag);
-        read_uint16(buf, buf_end, is_sub_final);
+
+        read_uint8(buf, buf_end, is_equivalence_type);
+        /* If there is an equivalence type, re-use it */
+        if (is_equivalence_type) {
+            uint8 u8;
+            /* padding */
+            read_uint8(buf, buf_end, u8);
+            (void)u8;
+
+            read_uint32(buf, buf_end, j);
+            if (module->types[j]->ref_count == UINT16_MAX) {
+                set_error_buf(error_buf, error_buf_size,
+                              "wasm type's ref count too large");
+                goto fail;
+            }
+            module->types[j]->ref_count++;
+            module->types[i] = module->types[j];
+            continue;
+        }
+
+        read_uint8(buf, buf_end, is_sub_final);
         read_uint32(buf, buf_end, parent_type_idx);
         read_uint16(buf, buf_end, rec_count);
         read_uint16(buf, buf_end, rec_idx);
@@ -1620,7 +1654,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
 
             types[i] = (AOTType *)func_type;
 
-            init_base_type((AOTType *)func_type, type_flag, is_sub_final,
+            init_base_type((AOTType *)func_type, i, type_flag, is_sub_final,
                            parent_type_idx, rec_count, rec_idx);
             func_type->param_count = param_count;
             func_type->result_count = result_count;
@@ -1726,7 +1760,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             offset = (uint32)offsetof(WASMStructObject, field_data);
             types[i] = (AOTType *)struct_type;
 
-            init_base_type((AOTType *)struct_type, type_flag, is_sub_final,
+            init_base_type((AOTType *)struct_type, i, type_flag, is_sub_final,
                            parent_type_idx, rec_count, rec_idx);
             struct_type->field_count = field_count;
             struct_type->ref_type_map_count = ref_type_map_count;
@@ -1812,7 +1846,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
 
             types[i] = (AOTType *)array_type;
 
-            init_base_type((AOTType *)array_type, type_flag, is_sub_final,
+            init_base_type((AOTType *)array_type, i, type_flag, is_sub_final,
                            parent_type_idx, rec_count, rec_idx);
             read_uint16(buf, buf_end, array_type->elem_flags);
             read_uint8(buf, buf_end, array_type->elem_type);
@@ -1841,7 +1875,6 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             if (rec_count == 0) {
                 bh_assert(rec_idx == 0);
             }
-
             for (j = i - rec_idx; j <= i; j++) {
                 AOTType *cur_type = module->types[j];
                 parent_type_idx = cur_type->parent_type_idx;
@@ -1850,6 +1883,11 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
 
                     module->types[j]->parent_type = parent_type;
                     module->types[j]->root_type = parent_type->root_type;
+                    if (parent_type->inherit_depth == UINT16_MAX) {
+                        set_error_buf(error_buf, error_buf_size,
+                                      "parent type's inherit depth too large");
+                        goto fail;
+                    }
                     module->types[j]->inherit_depth =
                         parent_type->inherit_depth + 1;
                 }
@@ -1867,7 +1905,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
                     AOTType *parent_type = module->types[parent_type_idx];
                     /* subtyping has been checked during compilation */
                     bh_assert(wasm_type_is_subtype_of(
-                        module->types[j], parent_type, module->types, i));
+                        module->types[j], parent_type, module->types, i + 1));
                     (void)parent_type;
                 }
             }

+ 1 - 0
core/iwasm/aot/aot_reloc.h

@@ -143,6 +143,7 @@ typedef struct {
     REG_SYM(aot_array_init_with_data),     \
     REG_SYM(aot_create_func_obj),          \
     REG_SYM(aot_obj_is_instance_of),       \
+    REG_SYM(aot_func_type_is_super_of),    \
     REG_SYM(aot_rtt_type_new),             \
     REG_SYM(wasm_array_obj_copy),          \
     REG_SYM(wasm_array_obj_new),           \

+ 20 - 2
core/iwasm/aot/aot_runtime.c

@@ -1721,6 +1721,7 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent,
 
         bh_assert(table_init_data);
 
+        bh_assert(table_init_data->table_index < module_inst->table_count);
         table = module_inst->tables[table_init_data->table_index];
         bh_assert(table);
 
@@ -1728,8 +1729,9 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent,
         bh_assert(table_data);
 
         wasm_runtime_get_table_inst_elem_type(
-            (WASMModuleInstanceCommon *)module_inst, i, &tbl_elem_type,
-            &tbl_elem_ref_type, &tbl_init_size, &tbl_max_size);
+            (WASMModuleInstanceCommon *)module_inst,
+            table_init_data->table_index, &tbl_elem_type, &tbl_elem_ref_type,
+            &tbl_init_size, &tbl_max_size);
 
         if (!wasm_elem_is_declarative(table_init_data->mode)
             && !wasm_reftype_is_subtype_of(
@@ -4487,6 +4489,22 @@ aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj,
     return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count);
 }
 
+bool
+aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1,
+                          uint32 type_idx2)
+{
+    AOTModule *aot_module = (AOTModule *)module_inst->module;
+    AOTType **types = aot_module->types;
+
+    if (type_idx1 == type_idx2)
+        return true;
+
+    bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC);
+    bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC);
+    return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1],
+                                      (WASMFuncType *)types[type_idx2]);
+}
+
 WASMRttTypeRef
 aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index)
 {

+ 5 - 0
core/iwasm/aot/aot_runtime.h

@@ -752,6 +752,11 @@ bool
 aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj,
                        uint32 type_index);
 
+/* Whether func type1 is one of super types of func type2 */
+bool
+aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1,
+                          uint32 type_idx2);
+
 WASMRttTypeRef
 aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index);
 

+ 6 - 1
core/iwasm/common/gc/gc_common.c

@@ -304,7 +304,12 @@ wasm_defined_type_equal(WASMType *const def_type1, WASMType *const def_type2,
     }
 #endif
 #if WASM_ENABLE_AOT != 0
-    /* TODO */
+    if (module->module_type == Wasm_Module_AoT) {
+        AOTModule *aot_module = (AOTModule *)module;
+
+        types = aot_module->types;
+        type_count = aot_module->type_count;
+    }
 #endif
 
     bh_assert(types);

+ 134 - 28
core/iwasm/common/gc/gc_type.c

@@ -250,6 +250,51 @@ wasm_value_types_is_subtype_of(const uint8 *types1,
     return true;
 }
 
+static bool
+rec_ref_type_equal(const WASMRefType *ref_type1, const WASMRefType *ref_type2,
+                   uint32 rec_begin_type_idx1, uint32 rec_begin_type_idx2,
+                   uint32 rec_count, const WASMTypePtr *types,
+                   uint32 type_count)
+{
+    uint32 type_idx1, type_idx2;
+
+    if (!wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)
+        || !wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common))
+        return ref_type1->ref_ht_common.heap_type
+                       == ref_type2->ref_ht_common.heap_type
+                   ? true
+                   : false;
+
+    /* Now both ref types are type of (ref type_idx) */
+    type_idx1 = ref_type1->ref_ht_typeidx.type_idx;
+    type_idx2 = ref_type2->ref_ht_typeidx.type_idx;
+
+    if (type_idx1 >= rec_begin_type_idx1
+        && type_idx1 < rec_begin_type_idx1 + rec_count) {
+        /* The converted iso-recursive types should be the same */
+        bool ret = (type_idx2 >= rec_begin_type_idx2
+                    && type_idx2 < rec_begin_type_idx2 + rec_count
+                    && type_idx1 - rec_begin_type_idx1
+                           == type_idx2 - rec_begin_type_idx2)
+                       ? true
+                       : false;
+        return ret;
+    }
+    else if (type_idx2 >= rec_begin_type_idx2
+             && type_idx2 < rec_begin_type_idx2 + rec_count) {
+        /* The converted iso-recursive types should be the same */
+        bool ret = (type_idx1 >= rec_begin_type_idx1
+                    && type_idx1 < rec_begin_type_idx1 + rec_count
+                    && type_idx1 - rec_begin_type_idx1
+                           == type_idx2 - rec_begin_type_idx2)
+                       ? true
+                       : false;
+        return ret;
+    }
+
+    return types[type_idx1] == types[type_idx2] ? true : false;
+}
+
 bool
 wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2,
                      const WASMTypePtr *types, uint32 type_count)
@@ -277,9 +322,11 @@ wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2,
 
             ref_type1 = type1->ref_type_maps[j].ref_type;
             ref_type2 = type2->ref_type_maps[j].ref_type;
-            if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1,
-                                    ref_type2->ref_type, ref_type2, types,
-                                    type_count))
+
+            if (!rec_ref_type_equal(
+                    ref_type1, ref_type2, type1->base_type.rec_begin_type_idx,
+                    type2->base_type.rec_begin_type_idx,
+                    type1->base_type.rec_count, types, type_count))
                 return false;
 
             j++;
@@ -316,9 +363,11 @@ wasm_struct_type_equal(const WASMStructType *type1, const WASMStructType *type2,
 
             ref_type1 = type1->ref_type_maps[j].ref_type;
             ref_type2 = type2->ref_type_maps[j].ref_type;
-            if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1,
-                                    ref_type2->ref_type, ref_type2, types,
-                                    type_count))
+
+            if (!rec_ref_type_equal(
+                    ref_type1, ref_type2, type1->base_type.rec_begin_type_idx,
+                    type2->base_type.rec_begin_type_idx,
+                    type1->base_type.rec_count, types, type_count))
                 return false;
 
             j++;
@@ -338,21 +387,67 @@ wasm_array_type_equal(const WASMArrayType *type1, const WASMArrayType *type2,
     if (type1->elem_flags != type2->elem_flags)
         return false;
 
-    return wasm_reftype_equal(type1->elem_type, type1->elem_ref_type,
-                              type2->elem_type, type2->elem_ref_type, types,
-                              type_count);
+    if (type1->elem_type != type2->elem_type)
+        return false;
+
+    if (!wasm_is_type_multi_byte_type(type1->elem_type))
+        return true;
+
+    return rec_ref_type_equal(type1->elem_ref_type, type2->elem_ref_type,
+                              type1->base_type.rec_begin_type_idx,
+                              type2->base_type.rec_begin_type_idx,
+                              type1->base_type.rec_count, types, type_count);
 }
 
 bool
 wasm_type_equal(const WASMType *type1, const WASMType *type2,
                 const WASMTypePtr *types, uint32 type_count)
 {
+    uint32 rec_begin_type_idx1 = type1->rec_begin_type_idx;
+    uint32 rec_begin_type_idx2 = type2->rec_begin_type_idx;
+    uint32 parent_type_idx1, parent_type_idx2, rec_count;
+
     if (type1 == type2)
         return true;
 
-    if (type1->type_flag != type2->type_flag)
+    if (!(type1->type_flag == type2->type_flag
+          && type1->is_sub_final == type2->is_sub_final
+          && type1->rec_count == type2->rec_count
+          && type1->rec_idx == type2->rec_idx))
         return false;
 
+    rec_count = type1->rec_count;
+
+    parent_type_idx1 = type1->parent_type_idx;
+    parent_type_idx2 = type2->parent_type_idx;
+
+    if (parent_type_idx1 >= rec_begin_type_idx1
+        && parent_type_idx1 < rec_begin_type_idx1 + rec_count) {
+        /* The converted iso-recursive types should be the same */
+        if (!(parent_type_idx2 >= rec_begin_type_idx2
+              && parent_type_idx2 < rec_begin_type_idx2 + rec_count
+              && parent_type_idx1 - rec_begin_type_idx1
+                     == parent_type_idx2 - rec_begin_type_idx2)) {
+            return false;
+        }
+    }
+    else if (parent_type_idx2 >= rec_begin_type_idx2
+             && parent_type_idx2 < rec_begin_type_idx2 + rec_count) {
+        /* The converted iso-recursive types should be the same */
+        if (!(parent_type_idx1 >= rec_begin_type_idx1
+              && parent_type_idx1 < rec_begin_type_idx1 + rec_count
+              && parent_type_idx1 - rec_begin_type_idx1
+                     == parent_type_idx2 - rec_begin_type_idx2)) {
+            return false;
+        }
+    }
+    else if (type1->parent_type != type2->parent_type) {
+        /* The parent types should be same since they have been
+           normalized and equivalence types with different type
+           indexes are referring to a same WASMType */
+        return false;
+    }
+
     if (wasm_type_is_func_type(type1))
         return wasm_func_type_equal((WASMFuncType *)type1,
                                     (WASMFuncType *)type2, types, type_count);
@@ -653,12 +748,6 @@ wasm_reftype_struct_size(const WASMRefType *ref_type)
     return (uint32)sizeof(RefHeapType_Common);
 }
 
-static bool
-type_idx_equal(uint32 type_idx1, uint32 type_idx2)
-{
-    return (type_idx1 == type_idx2) ? true : false;
-}
-
 bool
 wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1,
                        const RefHeapType_Common *ref_heap_type2,
@@ -673,8 +762,16 @@ wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1,
     if (ref_heap_type1->heap_type != ref_heap_type2->heap_type) {
         if (wasm_is_refheaptype_typeidx(ref_heap_type1)
             && wasm_is_refheaptype_typeidx(ref_heap_type2)) {
-            return type_idx_equal(ref_heap_type1->heap_type,
-                                  ref_heap_type2->heap_type);
+            if (ref_heap_type1->heap_type == ref_heap_type2->heap_type)
+                return true;
+            else
+                /* the type_count may be 0 when called from reftype_equal */
+                return ((uint32)ref_heap_type1->heap_type < type_count
+                        && (uint32)ref_heap_type2->heap_type < type_count
+                        && types[ref_heap_type1->heap_type]
+                               == types[ref_heap_type2->heap_type])
+                           ? true
+                           : false;
         }
         return false;
     }
@@ -835,6 +932,13 @@ wasm_type_is_supers_of(const WASMType *type1, const WASMType *type2)
     return false;
 }
 
+bool
+wasm_func_type_is_super_of(const WASMFuncType *type1, const WASMFuncType *type2)
+{
+    return wasm_type_is_supers_of((const WASMType *)type1,
+                                  (const WASMType *)type2);
+}
+
 bool
 wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1,
                            uint8 type2, const WASMRefType *ref_type2,
@@ -914,14 +1018,15 @@ wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1,
 #endif
     else if (type1 == REF_TYPE_HT_NULLABLE) {
         if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) {
+            bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count);
             /* reftype1 is (ref null $t) */
             if (type2 == REF_TYPE_HT_NULLABLE && ref_type2 != NULL
                 && wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) {
-                return type_idx_equal(ref_type1->ref_ht_typeidx.type_idx,
-                                      ref_type2->ref_ht_typeidx.type_idx)
-                       || wasm_type_is_supers_of(
-                           types[ref_type2->ref_ht_typeidx.type_idx],
-                           types[ref_type1->ref_ht_typeidx.type_idx]);
+                bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx
+                          < type_count);
+                return wasm_type_is_supers_of(
+                    types[ref_type2->ref_ht_typeidx.type_idx],
+                    types[ref_type1->ref_ht_typeidx.type_idx]);
             }
             else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag
                      == WASM_TYPE_STRUCT)
@@ -963,16 +1068,17 @@ wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1,
     else if (type1 == REF_TYPE_HT_NON_NULLABLE) {
         bh_assert(ref_type1);
         if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) {
+            bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count);
             /* reftype1 is (ref $t) */
             if ((type2 == REF_TYPE_HT_NULLABLE
                  || type2 == REF_TYPE_HT_NON_NULLABLE)
                 && ref_type2 != NULL
                 && wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) {
-                return type_idx_equal(ref_type1->ref_ht_typeidx.type_idx,
-                                      ref_type2->ref_ht_typeidx.type_idx)
-                       || wasm_type_is_supers_of(
-                           types[ref_type2->ref_ht_typeidx.type_idx],
-                           types[ref_type1->ref_ht_typeidx.type_idx]);
+                bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx
+                          < type_count);
+                return wasm_type_is_supers_of(
+                    types[ref_type2->ref_ht_typeidx.type_idx],
+                    types[ref_type1->ref_ht_typeidx.type_idx]);
             }
             else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag
                      == WASM_TYPE_STRUCT) {

+ 6 - 0
core/iwasm/common/gc/gc_type.h

@@ -47,6 +47,12 @@ wasm_func_type_is_subtype_of(const WASMFuncType *type1,
                              const WASMFuncType *type2,
                              const WASMTypePtr *types, uint32 type_count);
 
+/* Whether func type1 is one of super types of func type2,
+   used for the func type check in call_indirect/call_ref opcodes */
+bool
+wasm_func_type_is_super_of(const WASMFuncType *type1,
+                           const WASMFuncType *type2);
+
 /* Whether func type1's result types are subtype of
    func type2's result types */
 bool

+ 1 - 2
core/iwasm/common/wasm_application.c

@@ -578,8 +578,7 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name,
                         is_anyref = true;
                     }
 
-                    if (wasm_is_type_multi_byte_type(
-                            type->types[type->param_count + i])) {
+                    if (wasm_is_type_multi_byte_type(type->types[i])) {
                         WASMRefType *ref_type = ref_type_map->ref_type;
                         if (wasm_is_refheaptype_common(
                                 &ref_type->ref_ht_common)) {

+ 2 - 2
core/iwasm/common/wasm_native.c

@@ -84,9 +84,9 @@ compare_type_with_signautre(uint8 type, const char signature)
     if ('r' == signature
 #if WASM_ENABLE_GC != 0
 #if WASM_ENABLE_STRINGREF != 0
-        && (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_FUNCREF)
+        && (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_NULLFUNCREF)
 #else
-        && (type >= REF_TYPE_NULLREF && type <= REF_TYPE_FUNCREF)
+        && (type >= REF_TYPE_HT_NULLABLE && type <= REF_TYPE_NULLFUNCREF)
 #endif
 #else
         && type == VALUE_TYPE_EXTERNREF

+ 50 - 14
core/iwasm/compilation/aot_emit_aot_file.c

@@ -484,15 +484,15 @@ static uint32
 get_func_type_size(AOTCompContext *comp_ctx, AOTFuncType *func_type)
 {
 #if WASM_ENABLE_GC != 0
-    /* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + param
-     * count + result count
-     * + ref_type_map_count + types + context of ref_type_map */
+    /* type flag + equivalence type flag + is_sub_final + parent_type_idx
+       + rec_count + rec_idx + param count + result count
+       + ref_type_map_count + types + context of ref_type_map */
     if (comp_ctx->enable_gc) {
         uint32 size = 0;
 
         /* type flag */
         size += sizeof(func_type->base_type.type_flag);
-        /* is_sub_final */
+        /* equivalence type flag + is_sub_final */
         size += sizeof(uint16);
         /* parent_type_idx */
         size += sizeof(func_type->base_type.parent_type_idx);
@@ -529,12 +529,12 @@ static uint32
 get_struct_type_size(AOTCompContext *comp_ctx, AOTStructType *struct_type)
 {
     uint32 size = 0;
-    /* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + field
-     * count + fields */
+    /* type flag + equivalence type flag + is_sub_final + parent_type_idx
+       + rec_count + rec_idx + field count + fields */
 
     /* type flag */
     size += sizeof(struct_type->base_type.type_flag);
-    /* is_sub_final */
+    /* equivalence type flag + is_sub_final */
     size += sizeof(uint16);
     /* parent_type_idx */
     size += sizeof(struct_type->base_type.parent_type_idx);
@@ -558,12 +558,12 @@ static uint32
 get_array_type_size(AOTCompContext *comp_ctx, AOTArrayType *array_type)
 {
     uint32 size = 0;
-    /* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx +
-       elem_flags + elem_type + elem_ref_type */
+    /* type flag + equivalence type flag + is_sub_final + parent_type_idx
+       + rec_count + rec_idx + elem_flags + elem_type + elem_ref_type */
 
     /* type flag */
     size += sizeof(array_type->base_type.type_flag);
-    /* is_sub_final */
+    /* equivalence type flag + is_sub_final */
     size += sizeof(uint16);
     /* parent_type_idx (u32) */
     size += sizeof(array_type->base_type.parent_type_idx);
@@ -597,7 +597,22 @@ get_type_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data)
 #if WASM_ENABLE_GC != 0
     if (comp_ctx->enable_gc) {
         for (i = 0; i < comp_data->type_count; i++) {
+            uint32 j;
+
             size = align_uint(size, 4);
+
+            /* Emit simple info if there is an equivalence type */
+            for (j = 0; j < i; j++) {
+                if (comp_data->types[j] == comp_data->types[i]) {
+                    /* type_flag (2 bytes) + equivalence type flag (1 byte)
+                       + padding (1 byte) + equivalence type index */
+                    size += 8;
+                    break;
+                }
+            }
+            if (j < i)
+                continue;
+
             if (comp_data->types[i]->type_flag == WASM_TYPE_FUNC)
                 size += get_func_type_size(comp_ctx,
                                            (AOTFuncType *)comp_data->types[i]);
@@ -2093,13 +2108,32 @@ aot_emit_type_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
 
 #if WASM_ENABLE_GC != 0
     if (comp_ctx->enable_gc) {
-        int32 idx;
         AOTType **types = comp_data->types;
+        int32 idx;
+        uint32 j;
 
         for (i = 0; i < comp_data->type_count; i++) {
             offset = align_uint(offset, 4);
+
+            /* Emit simple info if there is an equivalence type */
+            for (j = 0; j < i; j++) {
+                if (types[j] == types[i]) {
+                    EMIT_U16(types[i]->type_flag);
+                    /* equivalence type flag is true */
+                    EMIT_U8(1);
+                    EMIT_U8(0);
+                    /* equivalence type index */
+                    EMIT_U32(j);
+                    break;
+                }
+            }
+            if (j < i)
+                continue;
+
             EMIT_U16(types[i]->type_flag);
-            EMIT_U16(types[i]->is_sub_final);
+            /* equivalence type flag is false */
+            EMIT_U8(0);
+            EMIT_U8(types[i]->is_sub_final);
             EMIT_U32(types[i]->parent_type_idx);
 
             EMIT_U16(types[i]->rec_count);
@@ -2593,7 +2627,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
     if (comp_ctx->enable_gc) {
         /* emit func_local_ref_flag arrays for both import and AOTed funcs */
         AOTFuncType *func_type;
-        uint32 j, local_ref_flags_cell_num;
+        uint32 j, local_ref_flags_cell_num, paddings;
 
         for (i = 0; i < comp_data->import_func_count; i++) {
             func_type = comp_data->import_funcs[i].func_type;
@@ -2603,6 +2637,8 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
                 local_ref_flags_cell_num += wasm_value_type_cell_num_internal(
                     func_type->types[j], comp_ctx->pointer_size);
             }
+            paddings =
+                local_ref_flags_cell_num < 2 ? 2 - local_ref_flags_cell_num : 0;
             local_ref_flags_cell_num =
                 local_ref_flags_cell_num > 2 ? local_ref_flags_cell_num : 2;
 
@@ -2614,7 +2650,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
                                        func_type->types[j]))
                     return false;
             }
-            for (; j < 2; j++)
+            for (j = 0; j < paddings; j++)
                 EMIT_U8(0);
         }
 

+ 80 - 14
core/iwasm/compilation/aot_emit_function.c

@@ -1826,6 +1826,52 @@ fail:
     return ret;
 }
 
+#if WASM_ENABLE_GC != 0
+static LLVMValueRef
+call_aot_func_type_is_super_of_func(AOTCompContext *comp_ctx,
+                                    AOTFuncContext *func_ctx,
+                                    LLVMValueRef type_idx1,
+                                    LLVMValueRef type_idx2)
+{
+    LLVMValueRef param_values[3], ret_value, value, func;
+    LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type;
+
+    param_types[0] = comp_ctx->aot_inst_type;
+    param_types[1] = I32_TYPE;
+    param_types[2] = I32_TYPE;
+    ret_type = INT8_TYPE;
+
+#if WASM_ENABLE_JIT != 0
+    if (comp_ctx->is_jit_mode)
+        GET_AOT_FUNCTION(llvm_jit_func_type_is_super_of, 3);
+    else
+#endif
+        GET_AOT_FUNCTION(aot_func_type_is_super_of, 3);
+
+    param_values[0] = func_ctx->aot_inst;
+    param_values[1] = type_idx1;
+    param_values[2] = type_idx2;
+
+    if (!(ret_value =
+              LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
+                             3, "call_aot_func_type_is_super_of"))) {
+        aot_set_last_error("llvm build call failed.");
+        return NULL;
+    }
+
+    if (!(ret_value = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, ret_value,
+                                    I8_ZERO, "check_fail"))) {
+        aot_set_last_error("llvm build icmp failed.");
+        return NULL;
+    }
+
+    return ret_value;
+
+fail:
+    return NULL;
+}
+#endif
+
 static bool
 call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
                             AOTFuncType *aot_func_type,
@@ -2018,15 +2064,23 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         return false;
     }
 
-    /* Find the equivalent function type whose type index is the smallest:
-       the callee function's type index is also converted to the smallest
-       one in wasm loader, so we can just check whether the two type indexes
-       are equal (the type index of call_indirect opcode and callee func),
-       we don't need to check whether the whole function types are equal,
-       including param types and result types. */
-    type_idx =
-        wasm_get_smallest_type_idx((WASMTypePtr *)comp_ctx->comp_data->types,
-                                   comp_ctx->comp_data->type_count, type_idx);
+    if (!comp_ctx->enable_gc) {
+        /* Find the equivalent function type whose type index is the smallest:
+           the callee function's type index is also converted to the smallest
+           one in wasm loader, so we can just check whether the two type indexes
+           are equal (the type index of call_indirect opcode and callee func),
+           we don't need to check whether the whole function types are equal,
+           including param types and result types. */
+        type_idx = wasm_get_smallest_type_idx(
+            (WASMTypePtr *)comp_ctx->comp_data->types,
+            comp_ctx->comp_data->type_count, type_idx);
+    }
+    else {
+        /* Call aot_func_type_is_super_of to check whether the func type
+           provided in the bytecode is a super type of the func type of
+           the function to call */
+    }
+
     ftype_idx_const = I32_CONST(type_idx);
     CHECK_LLVM_CONST(ftype_idx_const);
 
@@ -2254,11 +2308,23 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
-    /* Check if function type index not equal */
-    if (!(cmp_ftype_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx,
-                                        ftype_idx_const, "cmp_ftype_idx"))) {
-        aot_set_last_error("llvm build icmp failed.");
-        goto fail;
+#if WASM_ENABLE_GC != 0
+    if (comp_ctx->enable_gc) {
+        if (!(cmp_ftype_idx = call_aot_func_type_is_super_of_func(
+                  comp_ctx, func_ctx, ftype_idx_const, ftype_idx))) {
+            goto fail;
+        }
+    }
+    else
+#endif
+    {
+        /* Check if function type index not equal */
+        if (!(cmp_ftype_idx =
+                  LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx,
+                                ftype_idx_const, "cmp_ftype_idx"))) {
+            aot_set_last_error("llvm build icmp failed.");
+            goto fail;
+        }
     }
 
     /* Throw exception if ftype_idx != ftype_idx_const */

+ 11 - 8
core/iwasm/interpreter/wasm.h

@@ -274,7 +274,7 @@ typedef struct InitializerExpression {
  */
 typedef struct RefHeapType_TypeIdx {
     /* ref_type is REF_TYPE_HT_NULLABLE or
-       REF_TYPE_HT_NON_NULLABLE, (0x6C or 0x6B) */
+       REF_TYPE_HT_NON_NULLABLE, (0x63 or 0x64) */
     uint8 ref_type;
     /* true if ref_type is REF_TYPE_HT_NULLABLE */
     bool nullable;
@@ -288,7 +288,7 @@ typedef struct RefHeapType_TypeIdx {
  */
 typedef struct RefHeapType_Common {
     /* ref_type is REF_TYPE_HT_NULLABLE or
-       REF_TYPE_HT_NON_NULLABLE (0x6C or 0x6B) */
+       REF_TYPE_HT_NON_NULLABLE (0x63 or 0x64) */
     uint8 ref_type;
     /* true if ref_type is REF_TYPE_HT_NULLABLE */
     bool nullable;
@@ -338,18 +338,24 @@ typedef struct WASMType {
     uint16 type_flag;
 
     bool is_sub_final;
+    /* How many types are referring to this type */
+    uint16 ref_count;
     /* The inheritance depth */
-    uint32 inherit_depth;
+    uint16 inherit_depth;
     /* The root type */
     struct WASMType *root_type;
     /* The parent type */
     struct WASMType *parent_type;
     uint32 parent_type_idx;
 
-    /* number of internal types in the current rec group, if the type is not in
-     * a recursive group, rec_count = 0 */
+    /* The number of internal types in the current rec group, and if
+       the type is not in a recursive group, rec_count is 1 since a
+       single type definition is reinterpreted as a short-hand for a
+       recursive group containing just one type */
     uint16 rec_count;
     uint16 rec_idx;
+    /* The index of the begin type of this group */
+    uint32 rec_begin_type_idx;
 } WASMType, *WASMTypePtr;
 #endif /* end of WASM_ENABLE_GC */
 
@@ -375,9 +381,6 @@ typedef struct WASMFuncType {
     uint16 ref_type_map_count;
     WASMRefTypeMap *ref_type_maps;
     WASMRefTypeMap *result_ref_type_maps;
-    /* minimal type index of the type equal to this type,
-       used in type equal check in call_indirect opcode */
-    uint32 min_type_idx_normalized;
 #else
     uint16 ref_count;
 #endif

+ 2 - 2
core/iwasm/interpreter/wasm_interp_classic.c

@@ -2212,6 +2212,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 WASMFuncType *cur_type, *cur_func_type;
                 WASMTableInstance *tbl_inst;
                 uint32 tbl_idx;
+
 #if WASM_ENABLE_TAIL_CALL != 0
                 opcode = *(frame_ip - 1);
 #endif
@@ -2282,8 +2283,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     goto got_exception;
                 }
 #else
-                if (cur_type->min_type_idx_normalized
-                        != cur_func_type->min_type_idx_normalized) {
+                if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) {
                     wasm_set_exception(module, "indirect call type mismatch");
                     goto got_exception;
                 }

+ 1 - 2
core/iwasm/interpreter/wasm_interp_fast.c

@@ -1736,8 +1736,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     goto got_exception;
                 }
 #else
-                if (cur_type->min_type_idx_normalized
-                        != cur_func_type->min_type_idx_normalized) {
+                if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) {
                     wasm_set_exception(module, "indirect call type mismatch");
                     goto got_exception;
                 }

+ 167 - 84
core/iwasm/interpreter/wasm_loader.c

@@ -394,10 +394,10 @@ memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, char *error_buf,
 
 #if WASM_ENABLE_GC != 0
 static bool
-check_type_index(const WASMModule *module, uint32 type_index, char *error_buf,
-                 uint32 error_buf_size)
+check_type_index(const WASMModule *module, uint32 type_count, uint32 type_index,
+                 char *error_buf, uint32 error_buf_size)
 {
-    if (type_index >= module->type_count) {
+    if (type_index >= type_count) {
         set_error_buf_v(error_buf, error_buf_size, "unknown type %d",
                         type_index);
         return false;
@@ -409,7 +409,8 @@ static bool
 check_array_type(const WASMModule *module, uint32 type_index, char *error_buf,
                  uint32 error_buf_size)
 {
-    if (!check_type_index(module, type_index, error_buf, error_buf_size)) {
+    if (!check_type_index(module, module->type_count, type_index, error_buf,
+                          error_buf_size)) {
         return false;
     }
     if (module->types[type_index]->type_flag != WASM_TYPE_ARRAY) {
@@ -775,8 +776,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                 if (!is_byte_a_type(type1)) {
                     p--;
                     read_leb_uint32(p, p_end, type_idx);
-                    if (!check_type_index(module, type_idx, error_buf,
-                                          error_buf_size))
+                    if (!check_type_index(module, module->type_count, type_idx,
+                                          error_buf, error_buf_size))
                         goto fail;
 
                     wasm_set_refheaptype_typeidx(&cur_ref_type.ref_ht_typeidx,
@@ -902,7 +903,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         uint32 field_count;
                         read_leb_uint32(p, p_end, type_idx);
 
-                        if (!check_type_index(module, type_idx, error_buf,
+                        if (!check_type_index(module, module->type_count,
+                                              type_idx, error_buf,
                                               error_buf_size)) {
                             goto fail;
                         }
@@ -966,7 +968,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         read_leb_uint32(p, p_end, cur_value.type_index);
                         type_idx = cur_value.type_index;
 
-                        if (!check_type_index(module, type_idx, error_buf,
+                        if (!check_type_index(module, module->type_count,
+                                              type_idx, error_buf,
                                               error_buf_size)) {
                             goto fail;
                         }
@@ -1001,7 +1004,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         read_leb_uint32(p, p_end, cur_value.type_index);
                         type_idx = cur_value.type_index;
 
-                        if (!check_type_index(module, type_idx, error_buf,
+                        if (!check_type_index(module, module->type_count,
+                                              type_idx, error_buf,
                                               error_buf_size)) {
                             goto fail;
                         }
@@ -1275,6 +1279,13 @@ destroy_array_type(WASMArrayType *type)
 static void
 destroy_wasm_type(WASMType *type)
 {
+    if (type->ref_count > 1) {
+        /* The type is referenced by other types
+           of current wasm module */
+        type->ref_count--;
+        return;
+    }
+
     if (type->type_flag == WASM_TYPE_FUNC)
         destroy_func_type((WASMFuncType *)type);
     else if (type->type_flag == WASM_TYPE_STRUCT)
@@ -1289,8 +1300,9 @@ destroy_wasm_type(WASMType *type)
 /* Resolve (ref null ht) or (ref ht) */
 static bool
 resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end,
-                      WASMModule *module, bool nullable, WASMRefType *ref_type,
-                      char *error_buf, uint32 error_buf_size)
+                      WASMModule *module, uint32 type_count, bool nullable,
+                      WASMRefType *ref_type, char *error_buf,
+                      uint32 error_buf_size)
 {
     const uint8 *p = *p_buf, *p_end = buf_end;
 
@@ -1301,8 +1313,9 @@ resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end,
 
     if (wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common)) {
         /* heap type is (type i), i : typeidx, >= 0 */
-        if (!check_type_index(module, ref_type->ref_ht_typeidx.type_idx,
-                              error_buf, error_buf_size)) {
+        if (!check_type_index(module, type_count,
+                              ref_type->ref_ht_typeidx.type_idx, error_buf,
+                              error_buf_size)) {
             return false;
         }
     }
@@ -1320,9 +1333,10 @@ fail:
 
 static bool
 resolve_value_type(const uint8 **p_buf, const uint8 *buf_end,
-                   WASMModule *module, bool *p_need_ref_type_map,
-                   WASMRefType *ref_type, bool allow_packed_type,
-                   char *error_buf, uint32 error_buf_size)
+                   WASMModule *module, uint32 type_count,
+                   bool *p_need_ref_type_map, WASMRefType *ref_type,
+                   bool allow_packed_type, char *error_buf,
+                   uint32 error_buf_size)
 {
     const uint8 *p = *p_buf, *p_end = buf_end;
     uint8 type;
@@ -1334,8 +1348,8 @@ resolve_value_type(const uint8 **p_buf, const uint8 *buf_end,
 
     if (wasm_is_reftype_htref_nullable(type)) {
         /* (ref null ht) */
-        if (!resolve_reftype_htref(&p, p_end, module, true, ref_type, error_buf,
-                                   error_buf_size))
+        if (!resolve_reftype_htref(&p, p_end, module, type_count, true,
+                                   ref_type, error_buf, error_buf_size))
             return false;
         if (!wasm_is_refheaptype_common(&ref_type->ref_ht_common))
             *p_need_ref_type_map = true;
@@ -1351,8 +1365,8 @@ resolve_value_type(const uint8 **p_buf, const uint8 *buf_end,
     }
     else if (wasm_is_reftype_htref_non_nullable(type)) {
         /* (ref ht) */
-        if (!resolve_reftype_htref(&p, p_end, module, false, ref_type,
-                                   error_buf, error_buf_size))
+        if (!resolve_reftype_htref(&p, p_end, module, type_count, false,
+                                   ref_type, error_buf, error_buf_size))
             return false;
         *p_need_ref_type_map = true;
 #if WASM_ENABLE_STRINGREF != 0
@@ -1401,7 +1415,8 @@ reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type,
 
 static bool
 resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
-                  uint32 type_idx, char *error_buf, uint32 error_buf_size)
+                  uint32 type_count, uint32 type_idx, char *error_buf,
+                  uint32 error_buf_size)
 {
     const uint8 *p = *p_buf, *p_end = buf_end, *p_org;
     uint32 param_count, result_count, i, j = 0;
@@ -1417,8 +1432,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
     read_leb_uint32(p, p_end, param_count);
     p_org = p;
     for (i = 0; i < param_count; i++) {
-        if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                &ref_type, false, error_buf, error_buf_size)) {
+        if (!resolve_value_type(&p, p_end, module, type_count,
+                                &need_ref_type_map, &ref_type, false, error_buf,
+                                error_buf_size)) {
             return false;
         }
         if (need_ref_type_map)
@@ -1427,8 +1443,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
 
     read_leb_uint32(p, p_end, result_count);
     for (i = 0; i < result_count; i++) {
-        if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                &ref_type, false, error_buf, error_buf_size)) {
+        if (!resolve_value_type(&p, p_end, module, type_count,
+                                &need_ref_type_map, &ref_type, false, error_buf,
+                                error_buf_size)) {
             return false;
         }
         if (need_ref_type_map) {
@@ -1468,8 +1485,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
     }
 
     for (i = 0; i < param_count; i++) {
-        if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                &ref_type, false, error_buf, error_buf_size)) {
+        if (!resolve_value_type(&p, p_end, module, type_count,
+                                &need_ref_type_map, &ref_type, false, error_buf,
+                                error_buf_size)) {
             goto fail;
         }
         type->types[i] = ref_type.ref_type;
@@ -1485,8 +1503,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
 
     read_leb_uint32(p, p_end, result_count);
     for (i = 0; i < result_count; i++) {
-        if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                &ref_type, false, error_buf, error_buf_size)) {
+        if (!resolve_value_type(&p, p_end, module, type_count,
+                                &need_ref_type_map, &ref_type, false, error_buf,
+                                error_buf_size)) {
             goto fail;
         }
         type->types[param_count + i] = ref_type.ref_type;
@@ -1527,18 +1546,6 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
     }
 #endif
 
-    /* Calculate the minimal type index of the type equal to this type */
-    type->min_type_idx_normalized = type_idx;
-    for (i = 0; i < type_idx; i++) {
-        WASMFuncType *func_type = (WASMFuncType *)module->types[i];
-        if (func_type->base_type.type_flag == WASM_TYPE_FUNC
-            && wasm_func_type_equal(type, func_type, module->types,
-                                    type_idx + 1)) {
-            type->min_type_idx_normalized = i;
-            break;
-        }
-    }
-
     *p_buf = p;
 
     module->types[type_idx] = (WASMType *)type;
@@ -1552,8 +1559,8 @@ fail:
 
 static bool
 resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end,
-                    WASMModule *module, uint32 type_idx, char *error_buf,
-                    uint32 error_buf_size)
+                    WASMModule *module, uint32 type_count, uint32 type_idx,
+                    char *error_buf, uint32 error_buf_size)
 {
     const uint8 *p = *p_buf, *p_end = buf_end, *p_org;
     uint32 field_count, ref_type_map_count = 0, ref_field_count = 0;
@@ -1569,8 +1576,9 @@ resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end,
     read_leb_uint32(p, p_end, field_count);
     p_org = p;
     for (i = 0; i < field_count; i++) {
-        if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                &ref_type, true, error_buf, error_buf_size)) {
+        if (!resolve_value_type(&p, p_end, module, type_count,
+                                &need_ref_type_map, &ref_type, true, error_buf,
+                                error_buf_size)) {
             return false;
         }
         if (need_ref_type_map)
@@ -1617,8 +1625,9 @@ resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end,
 
     offset = (uint32)offsetof(WASMStructObject, field_data);
     for (i = 0; i < field_count; i++) {
-        if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                &ref_type, true, error_buf, error_buf_size)) {
+        if (!resolve_value_type(&p, p_end, module, type_count,
+                                &need_ref_type_map, &ref_type, true, error_buf,
+                                error_buf_size)) {
             goto fail;
         }
         type->fields[i].field_type = ref_type.ref_type;
@@ -1671,8 +1680,8 @@ fail:
 
 static bool
 resolve_array_type(const uint8 **p_buf, const uint8 *buf_end,
-                   WASMModule *module, uint32 type_idx, char *error_buf,
-                   uint32 error_buf_size)
+                   WASMModule *module, uint32 type_count, uint32 type_idx,
+                   char *error_buf, uint32 error_buf_size)
 {
     const uint8 *p = *p_buf, *p_end = buf_end;
     uint8 mutable;
@@ -1680,8 +1689,8 @@ resolve_array_type(const uint8 **p_buf, const uint8 *buf_end,
     WASMRefType ref_type;
     WASMArrayType *type = NULL;
 
-    if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, &ref_type,
-                            true, error_buf, error_buf_size)) {
+    if (!resolve_value_type(&p, p_end, module, type_count, &need_ref_type_map,
+                            &ref_type, true, error_buf, error_buf_size)) {
         return false;
     }
 
@@ -1730,7 +1739,8 @@ init_ref_type(WASMModule *module, WASMRefType *ref_type, bool nullable,
               int32 heap_type, char *error_buf, uint32 error_buf_size)
 {
     if (heap_type >= 0) {
-        if (!check_type_index(module, heap_type, error_buf, error_buf_size)) {
+        if (!check_type_index(module, module->type_count, heap_type, error_buf,
+                              error_buf_size)) {
             return false;
         }
         wasm_set_refheaptype_typeidx(&ref_type->ref_ht_typeidx, nullable,
@@ -2010,6 +2020,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
 
                 if (flag == DEFINED_TYPE_FUNC) {
                     if (!resolve_func_type(&p, buf_end, module,
+                                           processed_type_count + rec_count,
                                            processed_type_count + j, error_buf,
                                            error_buf_size)) {
                         return false;
@@ -2017,6 +2028,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                 }
                 else if (flag == DEFINED_TYPE_STRUCT) {
                     if (!resolve_struct_type(&p, buf_end, module,
+                                             processed_type_count + rec_count,
                                              processed_type_count + j,
                                              error_buf, error_buf_size)) {
                         return false;
@@ -2024,6 +2036,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                 }
                 else if (flag == DEFINED_TYPE_ARRAY) {
                     if (!resolve_array_type(&p, buf_end, module,
+                                            processed_type_count + rec_count,
                                             processed_type_count + j, error_buf,
                                             error_buf_size)) {
                         return false;
@@ -2037,13 +2050,13 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
 
                 cur_type = module->types[processed_type_count + j];
 
+                cur_type->ref_count = 1;
                 cur_type->parent_type_idx = parent_type_idx;
                 cur_type->is_sub_final = is_sub_final;
 
-                if (rec_count > 1) {
-                    cur_type->rec_count = rec_count;
-                    cur_type->rec_idx = j;
-                }
+                cur_type->rec_count = rec_count;
+                cur_type->rec_idx = j;
+                cur_type->rec_begin_type_idx = processed_type_count;
             }
 
             /* resolve subtyping relationship in current rec group */
@@ -2055,6 +2068,11 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                         module->types[cur_type->parent_type_idx];
                     cur_type->parent_type = parent_type;
                     cur_type->root_type = parent_type->root_type;
+                    if (parent_type->inherit_depth == UINT16_MAX) {
+                        set_error_buf(error_buf, error_buf_size,
+                                      "parent type's inherit depth too large");
+                        return false;
+                    }
                     cur_type->inherit_depth = parent_type->inherit_depth + 1;
                 }
                 else {
@@ -2080,6 +2098,49 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                 }
             }
 
+            /* If there is already an equivalence type or a group of equivalence
+               recursive types created, use it or them instead */
+            for (j = 0; j < processed_type_count;) {
+                WASMType *src_type = module->types[j];
+                WASMType *cur_type = module->types[processed_type_count];
+                uint32 k, src_rec_count;
+
+                src_rec_count = src_type->rec_count;
+                if (src_rec_count != rec_count) {
+                    /* no type equivalence */
+                    j += src_rec_count;
+                    continue;
+                }
+
+                for (k = 0; k < rec_count; k++) {
+                    src_type = module->types[j + k];
+                    cur_type = module->types[processed_type_count + k];
+                    if (!wasm_type_equal(src_type, cur_type, module->types,
+                                         module->type_count)) {
+                        break;
+                    }
+                }
+                if (k < rec_count) {
+                    /* no type equivalence */
+                    j += src_rec_count;
+                    continue;
+                }
+
+                /* type equivalence */
+                for (k = 0; k < rec_count; k++) {
+                    if (module->types[j + k]->ref_count == UINT16_MAX) {
+                        set_error_buf(error_buf, error_buf_size,
+                                      "wasm type's ref count too large");
+                        return false;
+                    }
+                    destroy_wasm_type(module->types[processed_type_count + k]);
+                    module->types[processed_type_count + k] =
+                        module->types[j + k];
+                    module->types[j + k]->ref_count++;
+                }
+                break;
+            }
+
             if (rec_count > 1) {
                 LOG_VERBOSE("Finished processing rec group [%d-%d]",
                             processed_type_count,
@@ -2511,8 +2572,9 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
         return false;
     }
 #else /* else of WASM_ENABLE_GC == 0 */
-    if (!resolve_value_type(&p, p_end, parent_module, &need_ref_type_map,
-                            &ref_type, false, error_buf, error_buf_size)) {
+    if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count,
+                            &need_ref_type_map, &ref_type, false, error_buf,
+                            error_buf_size)) {
         return false;
     }
     if (wasm_is_reftype_htref_non_nullable(ref_type.ref_type)) {
@@ -2947,8 +3009,9 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end,
     declare_type = read_uint8(p);
     declare_mutable = read_uint8(p);
 #else
-    if (!resolve_value_type(&p, p_end, parent_module, &need_ref_type_map,
-                            &ref_type, false, error_buf, error_buf_size)) {
+    if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count,
+                            &need_ref_type_map, &ref_type, false, error_buf,
+                            error_buf_size)) {
         return false;
     }
     declare_type = ref_type.ref_type;
@@ -3050,8 +3113,9 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
         return false;
     }
 #else /* else of WASM_ENABLE_GC == 0 */
-    if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, &ref_type,
-                            false, error_buf, error_buf_size)) {
+    if (!resolve_value_type(&p, p_end, module, module->type_count,
+                            &need_ref_type_map, &ref_type, false, error_buf,
+                            error_buf_size)) {
         return false;
     }
     table->elem_type = ref_type.ref_type;
@@ -3536,7 +3600,8 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
             type_index_org = type_index;
 #endif
 
-#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0)
+#if (WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0) \
+    && WASM_ENABLE_GC == 0
             type_index = wasm_get_smallest_type_idx(
                 module->types, module->type_count, type_index);
 #endif
@@ -3577,8 +3642,9 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
 #endif
 #else
                 if (!resolve_value_type(&p_code, buf_code_end, module,
-                                        &need_ref_type_map, &ref_type, false,
-                                        error_buf, error_buf_size)) {
+                                        module->type_count, &need_ref_type_map,
+                                        &ref_type, false, error_buf,
+                                        error_buf_size)) {
                     return false;
                 }
                 local_count += sub_local_count;
@@ -3664,8 +3730,9 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
                 }
 #else
                 if (!resolve_value_type(&p_code, buf_code_end, module,
-                                        &need_ref_type_map, &ref_type, false,
-                                        error_buf, error_buf_size)) {
+                                        module->type_count, &need_ref_type_map,
+                                        &ref_type, false, error_buf,
+                                        error_buf_size)) {
                     return false;
                 }
                 if (need_ref_type_map) {
@@ -3923,9 +3990,9 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
             global->type = read_uint8(p);
             mutable = read_uint8(p);
 #else
-            if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
-                                    &ref_type, false, error_buf,
-                                    error_buf_size)) {
+            if (!resolve_value_type(&p, p_end, module, module->type_count,
+                                    &need_ref_type_map, &ref_type, false,
+                                    error_buf, error_buf_size)) {
                 return false;
             }
             global->type = ref_type.ref_type;
@@ -4231,8 +4298,8 @@ load_elem_type(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 #else
     p--;
     if (!resolve_value_type((const uint8 **)&p, p_end, module,
-                            &need_ref_type_map, &elem_ref_type, false,
-                            error_buf, error_buf_size)) {
+                            module->type_count, &need_ref_type_map,
+                            &elem_ref_type, false, error_buf, error_buf_size)) {
         return false;
     }
     if (!wasm_is_type_reftype(elem_ref_type.ref_type)) {
@@ -10827,7 +10894,8 @@ re_scan:
                         p_org = p;
                         p--;
                         if (!resolve_value_type((const uint8 **)&p, p_end,
-                                                module, &need_ref_type_map,
+                                                module, module->type_count,
+                                                &need_ref_type_map,
                                                 &wasm_ref_type, false,
                                                 error_buf, error_buf_size)) {
                             goto fail;
@@ -11303,8 +11371,8 @@ re_scan:
                     bh_memcpy_s(loader_ctx->frame_offset, size,
                                 block->param_frame_offsets, size);
                     loader_ctx->frame_offset += (size / sizeof(int16));
-                    loader_ctx->dynamic_offset = block->start_dynamic_offset;
                 }
+                loader_ctx->dynamic_offset = block->start_dynamic_offset;
 #endif
 
                 break;
@@ -11598,6 +11666,15 @@ re_scan:
                 if (opcode == WASM_OP_CALL_REF
                     || opcode == WASM_OP_RETURN_CALL_REF) {
                     read_leb_uint32(p, p_end, type_idx1);
+                    if (!check_type_index(module, module->type_count, type_idx1,
+                                          error_buf, error_buf_size)) {
+                        goto fail;
+                    }
+                    if (module->types[type_idx1]->type_flag != WASM_TYPE_FUNC) {
+                        set_error_buf(error_buf, error_buf_size,
+                                      "unkown function type");
+                        goto fail;
+                    }
                     if (!wasm_loader_pop_nullable_typeidx(loader_ctx, &type,
                                                           &type_idx, error_buf,
                                                           error_buf_size)) {
@@ -11606,8 +11683,8 @@ re_scan:
                     if (type == VALUE_TYPE_ANY) {
                         type_idx = type_idx1;
                     }
-                    if (!check_type_index(module, type_idx, error_buf,
-                                          error_buf_size)) {
+                    if (!check_type_index(module, module->type_count, type_idx,
+                                          error_buf, error_buf_size)) {
                         goto fail;
                     }
                     if (module->types[type_idx]->type_flag != WASM_TYPE_FUNC) {
@@ -11615,7 +11692,9 @@ re_scan:
                                       "unkown function type");
                         goto fail;
                     }
-                    if (type_idx != type_idx1) {
+                    if (!wasm_func_type_is_super_of(
+                            (WASMFuncType *)module->types[type_idx1],
+                            (WASMFuncType *)module->types[type_idx])) {
                         set_error_buf(error_buf, error_buf_size,
                                       "function type mismatch");
                         goto fail;
@@ -12055,8 +12134,9 @@ re_scan:
 #else
                 p_org = p + 1;
                 if (!resolve_value_type((const uint8 **)&p, p_end, module,
-                                        &need_ref_type_map, &wasm_ref_type,
-                                        false, error_buf, error_buf_size)) {
+                                        module->type_count, &need_ref_type_map,
+                                        &wasm_ref_type, false, error_buf,
+                                        error_buf_size)) {
                     goto fail;
                 }
                 type = wasm_ref_type.ref_type;
@@ -12223,8 +12303,8 @@ re_scan:
 #else
                 read_leb_int32(p, p_end, heap_type);
                 if (heap_type >= 0) {
-                    if (!check_type_index(module, heap_type, error_buf,
-                                          error_buf_size)) {
+                    if (!check_type_index(module, module->type_count, heap_type,
+                                          error_buf, error_buf_size)) {
                         goto fail;
                     }
                     wasm_set_refheaptype_typeidx(&wasm_ref_type.ref_ht_typeidx,
@@ -13288,7 +13368,8 @@ re_scan:
 #if WASM_ENABLE_FAST_INTERP != 0
                         emit_uint32(loader_ctx, type_idx);
 #endif
-                        if (!check_type_index(module, type_idx, error_buf,
+                        if (!check_type_index(module, module->type_count,
+                                              type_idx, error_buf,
                                               error_buf_size)) {
                             goto fail;
                         }
@@ -13374,7 +13455,8 @@ re_scan:
 #if WASM_ENABLE_FAST_INTERP != 0
                         emit_uint32(loader_ctx, type_idx);
 #endif
-                        if (!check_type_index(module, type_idx, error_buf,
+                        if (!check_type_index(module, module->type_count,
+                                              type_idx, error_buf,
                                               error_buf_size)) {
                             goto fail;
                         }
@@ -13787,7 +13869,8 @@ re_scan:
                         emit_uint32(loader_ctx, (uint32)heap_type);
 #endif
                         if (heap_type >= 0) {
-                            if (!check_type_index(module, heap_type, error_buf,
+                            if (!check_type_index(module, module->type_count,
+                                                  heap_type, error_buf,
                                                   error_buf_size)) {
                                 goto fail;
                             }

+ 1 - 1
core/iwasm/interpreter/wasm_mini_loader.c

@@ -6226,8 +6226,8 @@ re_scan:
                     bh_memcpy_s(loader_ctx->frame_offset, size,
                                 block->param_frame_offsets, size);
                     loader_ctx->frame_offset += (size / sizeof(int16));
-                    loader_ctx->dynamic_offset = block->start_dynamic_offset;
                 }
+                loader_ctx->dynamic_offset = block->start_dynamic_offset;
 #endif
 
                 break;

+ 16 - 0
core/iwasm/interpreter/wasm_runtime.c

@@ -4404,6 +4404,22 @@ llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst,
     return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count);
 }
 
+bool
+llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst,
+                               uint32 type_idx1, uint32 type_idx2)
+{
+    WASMModule *module = module_inst->module;
+    WASMType **types = module->types;
+
+    if (type_idx1 == type_idx2)
+        return true;
+
+    bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC);
+    bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC);
+    return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1],
+                                      (WASMFuncType *)types[type_idx2]);
+}
+
 WASMRttTypeRef
 llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index)
 {

+ 5 - 0
core/iwasm/interpreter/wasm_runtime.h

@@ -811,6 +811,11 @@ bool
 llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst,
                             WASMObjectRef gc_obj, uint32 type_index);
 
+/* Whether func type1 is one of super types of func type2 */
+bool
+llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst,
+                               uint32 type_idx1, uint32 type_idx2);
+
 WASMRttTypeRef
 llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index);
 

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

@@ -95,7 +95,7 @@ def ignore_the_case(
         return True
 
     if gc_flag:
-        if case_name in ["type-equivalence", "type-rec", "array_init_elem", "array_init_data"]:
+        if case_name in ["array_init_elem", "array_init_data"]:
             return True
 
     if sgx_flag:

+ 4 - 287
tests/wamr-test-suites/spec-test-script/gc_ignore_cases.patch

@@ -9,78 +9,6 @@ index 335496f0..5b975028 100644
 -  "integer representation too long"
 +  "invalid type flag" ;; In GC extension, the first byte in rectype define is just one byte, not LEB128 encoded.
  )
-diff --git a/test/core/binary.wast b/test/core/binary.wast
-index 1661a1c6..84c716b9 100644
---- a/test/core/binary.wast
-+++ b/test/core/binary.wast
-@@ -1082,7 +1082,7 @@
- )
- 
- ;; 1 br_table target declared, 2 given
--(assert_malformed
-+(;assert_malformed
-   (module binary
-     "\00asm" "\01\00\00\00"
-     "\01\04\01"                             ;; type section
-@@ -1132,3 +1132,4 @@
-   )
-   "unexpected content after last section"
- )
-+;)
-diff --git a/test/core/data.wast b/test/core/data.wast
-index a5c87fbb..6f948bae 100644
---- a/test/core/data.wast
-+++ b/test/core/data.wast
-@@ -306,9 +306,10 @@
-     "\02\01\41\00\0b"                       ;; active data segment 0 for memory 1
-     "\00"                                   ;; empty vec(byte)
-   )
--  "unknown memory 1"
-+  "unknown memory"
- )
- 
-+(; not supported by wat2wasm
- ;; Data segment with memory index 0 (no memory section)
- (assert_invalid
-   (module binary
-@@ -317,7 +318,7 @@
-     "\00\41\00\0b"                          ;; active data segment 0 for memory 0
-     "\00"                                   ;; empty vec(byte)
-   )
--  "unknown memory 0"
-+  "unknown memory"
- )
- 
- ;; Data segment with memory index 1 (no memory section)
-@@ -328,7 +329,7 @@
-     "\02\01\41\00\0b"                       ;; active data segment 0 for memory 1
-     "\00"                                   ;; empty vec(byte)
-   )
--  "unknown memory 1"
-+  "unknown memory"
- )
- 
- ;; Data segment with memory index 1 and vec(byte) as above,
-@@ -348,7 +349,7 @@
-     "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f"
-     "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d"
-   )
--  "unknown memory 1"
-+  "unknown memory"
- )
- 
- ;; Data segment with memory index 1 and specially crafted vec(byte) after.
-@@ -368,8 +369,9 @@
-     "\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f"
-     "\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d"
-   )
--  "unknown memory 1"
-+  "unknown memory"
- )
-+;)
- 
- 
- ;; Invalid offsets
 diff --git a/test/core/elem.wast b/test/core/elem.wast
 index df1610f6..32c1d8b3 100644
 --- a/test/core/elem.wast
@@ -268,196 +196,11 @@ index 00000000..32650644
 +  )
 +  "unsupported initializer expression for table"
 +)
-diff --git a/test/core/gc/ref_test.wast b/test/core/gc/ref_test.wast
-index 590b81b8..e0aa49ed 100644
---- a/test/core/gc/ref_test.wast
-+++ b/test/core/gc/ref_test.wast
-@@ -310,15 +310,16 @@
-       (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 11)))))
-       (br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 12)))))
- 
--      (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1)))))
--      (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2)))))
-+      ;; Must have explicit sub relationship
-+      ;; (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1)))))
-+      ;; (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2)))))
- 
--      (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11)))))
--      (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12)))))
-+      ;; (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11)))))
-+      ;; (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12)))))
- 
--      (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2)))))
-+      ;; (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2)))))
- 
--      (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12)))))
-+      ;; (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12)))))
- 
-       (return)
-     )
 diff --git a/test/core/gc/type-subtyping.wast b/test/core/gc/type-subtyping.wast
-index a9022fc3..4e22e91b 100644
+index a9022fc3..4aa36e2a 100644
 --- a/test/core/gc/type-subtyping.wast
 +++ b/test/core/gc/type-subtyping.wast
-@@ -112,6 +112,8 @@
-   )
- )
- 
-+;; don't support recursive type equality and subtype check
-+(;
- (module
-   (rec (type $f1 (sub (func))) (type (struct (field (ref $f1)))))
-   (rec (type $f2 (sub (func))) (type (struct (field (ref $f2)))))
-@@ -135,6 +137,7 @@
-   (func $g (type $g2))
-   (global (ref $g1) (ref.func $g))
- )
-+;)
- 
- (assert_invalid
-   (module
-@@ -156,6 +159,8 @@
-   (global (ref $f1) (ref.func $g))
- )
- 
-+;; don't support recursive type equality and subtype check
-+(;
- (module
-   (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
-   (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
-@@ -201,6 +206,7 @@
-   (global (ref $g12) (ref.func $g12))
-   (global (ref $g22) (ref.func $g12))
- )
-+;)
- 
- (assert_invalid
-   (module
-@@ -226,6 +232,8 @@
- 
- ;; Runtime types
- 
-+;; don't support recursive type equality and subtype check
-+(;
- (module
-   (type $t0 (sub (func (result (ref null func)))))
-   (rec (type $t1 (sub $t0 (func (result (ref null $t1))))))
-@@ -286,6 +294,7 @@
- (assert_trap (invoke "fail4") "cast")
- (assert_trap (invoke "fail5") "cast")
- (assert_trap (invoke "fail6") "cast")
-+;)
- 
- (module
-   (type $t1 (sub (func)))
-@@ -316,7 +325,8 @@
- (assert_trap (invoke "fail3") "cast")
- (assert_trap (invoke "fail4") "cast")
- 
--
-+;; don't support recursive type equality and subtype check
-+(;
- (module
-   (rec (type $f1 (sub (func))) (type (struct (field (ref $f1)))))
-   (rec (type $f2 (sub (func))) (type (struct (field (ref $f2)))))
-@@ -346,6 +356,7 @@
-   )
- )
- (assert_return (invoke "run") (i32.const 1))
-+;)
- 
- (module
-   (rec (type $f1 (sub (func))) (type (struct (field (ref $f1)))))
-@@ -370,6 +381,8 @@
- )
- (assert_return (invoke "run") (i32.const 1))
- 
-+;; don't support recursive type equality and subtype check
-+(;
- (module
-   (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
-   (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
-@@ -390,7 +403,6 @@
- )
- (assert_return (invoke "run") (i32.const 1) (i32.const 1))
- 
--
- (module
-   (rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11))))))
-   (rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21))))))
-@@ -429,7 +441,9 @@
-   (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1)
-   (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1)
- )
-+;)
- 
-+(; we use normalized function type index
- (module
-   (rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func))))
-   (rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func))))
-@@ -439,6 +453,7 @@
-   )
- )
- (assert_return (invoke "run") (i32.const 0))
-+;)
- 
- (module
-   (rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func))))
-@@ -547,15 +562,15 @@
-   (func (import "M3" "g") (type $g1))
- )
- 
--(module
--  (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
--  (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
--  (rec
--    (type $g2 (sub $f2 (func)))
--    (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
--  )
--  (func (export "g") (type $g2))
--)
-+;; (module
-+;;   (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
-+;;   (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
-+;;   (rec
-+;;     (type $g2 (sub $f2 (func)))
-+;;     (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
-+;;   )
-+;;   (func (export "g") (type $g2))
-+;; )
- (register "M4")
- (module
-   (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
-@@ -597,17 +612,17 @@
-   (func (import "M6" "g") (type $f1))
- )
- 
--(module
--  (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
--  (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
--  (rec
--    (type $g2 (sub $f2 (func)))
--    (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
--  )
--  (rec (type $h (sub $g2 (func))) (type (struct)))
--  (func (export "h") (type $h))
--)
--(register "M7")
-+;; (module
-+;;   (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
-+;;   (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
-+;;   (rec
-+;;     (type $g2 (sub $f2 (func)))
-+;;     (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
-+;;   )
-+;;   (rec (type $h (sub $g2 (func))) (type (struct)))
-+;;   (func (export "h") (type $h))
-+;; )
-+;; (register "M7")
- (module
-   (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
-   (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
-@@ -740,7 +755,7 @@
+@@ -740,7 +740,7 @@
    "sub type"
  )
  
@@ -466,7 +209,7 @@ index a9022fc3..4e22e91b 100644
    (module
      (type $f0 (sub (func (param i32) (result i32))))
      (type $s0 (sub $f0 (struct)))
-@@ -764,7 +779,7 @@
+@@ -764,7 +764,7 @@
    "sub type"
  )
  
@@ -475,7 +218,7 @@ index a9022fc3..4e22e91b 100644
    (module
      (type $s0 (sub (struct)))
      (type $f0 (sub $s0 (func (param i32) (result i32))))
-@@ -772,7 +787,7 @@
+@@ -772,7 +772,7 @@
    "sub type"
  )
  
@@ -1262,29 +1005,3 @@ index 0b2d26f7..bdab6a01 100644
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
    (elem (table $t1) (i32.const 2) func 3 1 4 1)
-diff --git a/test/core/unreached-valid.wast b/test/core/unreached-valid.wast
-index f3feb0f3..d8ef8743 100644
---- a/test/core/unreached-valid.wast
-+++ b/test/core/unreached-valid.wast
-@@ -60,7 +60,7 @@
- 
- ;; Validation after unreachable
- 
--(module
-+(;module
-   (func (export "meet-bottom")
-     (block (result f64)
-       (block (result f32)
-@@ -76,7 +76,6 @@
- 
- (assert_trap (invoke "meet-bottom") "unreachable")
- 
--
- ;; Bottom heap type
- 
- (module
-@@ -106,3 +105,4 @@
-     (unreachable)
-   )
- )
-+;)