Просмотр исходного кода

Support table64 extension in classic-interp and AOT running modes (#3811)

Add table64 extension(in Memory64 proposal) support in classic-interp
and AOT running modes, currently still use uint32 to represent table's
initial and maximum size to keep AOT ABI unchanged.
TianlongLiang 1 год назад
Родитель
Сommit
36d438051e

+ 6 - 0
core/iwasm/aot/aot_loader.c

@@ -1382,6 +1382,12 @@ load_table_list(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
     for (i = 0; i < module->table_count; i++, table++) {
         read_uint8(buf, buf_end, table->table_type.elem_type);
         read_uint8(buf, buf_end, table->table_type.flags);
+
+        if (!wasm_table_check_flags(table->table_type.flags, error_buf,
+                                    error_buf_size, true)) {
+            return false;
+        }
+
         read_uint8(buf, buf_end, table->table_type.possible_grow);
 #if WASM_ENABLE_GC != 0
         if (wasm_is_type_multi_byte_type(table->table_type.elem_type)) {

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

@@ -737,18 +737,24 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
 
 #if WASM_ENABLE_REF_TYPES != 0
         bh_assert(
-            table_seg->offset.init_expr_type == INIT_EXPR_TYPE_I32_CONST
+            table_seg->offset.init_expr_type
+                == (tbl_inst->is_table64 ? INIT_EXPR_TYPE_I64_CONST
+                                         : INIT_EXPR_TYPE_I32_CONST)
             || table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL
             || table_seg->offset.init_expr_type == INIT_EXPR_TYPE_FUNCREF_CONST
             || table_seg->offset.init_expr_type
                    == INIT_EXPR_TYPE_REFNULL_CONST);
 #else
-        bh_assert(table_seg->offset.init_expr_type == INIT_EXPR_TYPE_I32_CONST
+        bh_assert(table_seg->offset.init_expr_type
+                      == (tbl_inst->is_table64 ? INIT_EXPR_TYPE_I64_CONST
+                                               : INIT_EXPR_TYPE_I32_CONST)
                   || table_seg->offset.init_expr_type
                          == INIT_EXPR_TYPE_GET_GLOBAL);
 #endif
 
         /* Resolve table data base offset */
+        /* TODO: The table64 current implementation assumes table max size
+         * UINT32_MAX, so the offset conversion here is safe */
         if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
             global_index = table_seg->offset.u.global_index;
 

+ 62 - 0
core/iwasm/common/wasm_loader_common.c

@@ -19,6 +19,37 @@ wasm_loader_set_error_buf(char *error_buf, uint32 error_buf_size,
     }
 }
 
+#if WASM_ENABLE_MEMORY64 != 0
+bool
+check_memory64_flags_consistency(WASMModule *module, char *error_buf,
+                                 uint32 error_buf_size, bool is_aot)
+{
+    uint32 i;
+    bool wasm64_flag, all_wasm64 = true, none_wasm64 = true;
+
+    for (i = 0; i < module->import_memory_count; ++i) {
+        wasm64_flag =
+            module->import_memories[i].u.memory.mem_type.flags & MEMORY64_FLAG;
+        all_wasm64 &= wasm64_flag;
+        none_wasm64 &= !wasm64_flag;
+    }
+
+    for (i = 0; i < module->memory_count; ++i) {
+        wasm64_flag = module->memories[i].flags & MEMORY64_FLAG;
+        all_wasm64 &= wasm64_flag;
+        none_wasm64 &= !wasm64_flag;
+    }
+
+    if (!(all_wasm64 || none_wasm64)) {
+        wasm_loader_set_error_buf(
+            error_buf, error_buf_size,
+            "inconsistent limits wasm64 flags for memory sections", is_aot);
+        return false;
+    }
+    return true;
+}
+#endif
+
 bool
 wasm_memory_check_flags(const uint8 mem_flag, char *error_buf,
                         uint32 error_buf_size, bool is_aot)
@@ -60,6 +91,37 @@ wasm_memory_check_flags(const uint8 mem_flag, char *error_buf,
     return true;
 }
 
+bool
+wasm_table_check_flags(const uint8 table_flag, char *error_buf,
+                       uint32 error_buf_size, bool is_aot)
+{
+    /* Check whether certain features indicated by mem_flag are enabled in
+     * runtime */
+    if (table_flag > MAX_TABLE_SIZE_FLAG) {
+        if (table_flag & SHARED_TABLE_FLAG) {
+            wasm_loader_set_error_buf(error_buf, error_buf_size,
+                                      "tables cannot be shared", is_aot);
+        }
+#if WASM_ENABLE_MEMORY64 == 0
+        if (table_flag & TABLE64_FLAG) {
+            wasm_loader_set_error_buf(error_buf, error_buf_size,
+                                      "invalid limits flags(table64 flag was "
+                                      "found, please enable memory64)",
+                                      is_aot);
+            return false;
+        }
+#endif
+    }
+
+    if (table_flag > MAX_TABLE_SIZE_FLAG + TABLE64_FLAG) {
+        wasm_loader_set_error_buf(error_buf, error_buf_size,
+                                  "invalid limits flags", is_aot);
+        return false;
+    }
+
+    return true;
+}
+
 /*
  * compare with a bigger type set in `wasm_value_type_size_internal()`,
  * this function will only cover global value type, function's param

+ 12 - 0
core/iwasm/common/wasm_loader_common.h

@@ -13,10 +13,22 @@
 extern "C" {
 #endif
 
+#if WASM_ENABLE_MEMORY64 != 0
+/* check consistency of memory64 flags across all memories,
+ * they must be either all wasm64 or all wasm32 */
+bool
+check_memory64_flags_consistency(WASMModule *module, char *error_buf,
+                                 uint32 error_buf_size, bool is_aot);
+#endif
+
 bool
 wasm_memory_check_flags(const uint8 mem_flag, char *error_buf,
                         uint32 error_buf_size, bool is_aot);
 
+bool
+wasm_table_check_flags(const uint8 table_flag, char *error_buf,
+                       uint32 error_buf_size, bool is_aot);
+
 bool
 is_valid_value_type(uint8 value_tpye);
 

+ 14 - 3
core/iwasm/compilation/aot_compiler.c

@@ -146,9 +146,20 @@ aot_validate_wasm(AOTCompContext *comp_ctx)
     }
 
 #if WASM_ENABLE_MEMORY64 != 0
-    if (comp_ctx->pointer_size < sizeof(uint64) && IS_MEMORY64) {
-        aot_set_last_error("Compiling wasm64 to 32bit platform is not allowed");
-        return false;
+    if (comp_ctx->pointer_size < sizeof(uint64)) {
+        if (IS_MEMORY64) {
+            aot_set_last_error("Compiling wasm64(contains i64 memory section) "
+                               "to 32bit platform is not allowed");
+            return false;
+        }
+
+        for (uint32 i = 0; i < comp_ctx->comp_data->table_count; ++i) {
+            if (IS_TABLE64(i)) {
+                aot_set_last_error("Compiling wasm64(contains i64 table "
+                                   "section) to 32bit platform is not allowed");
+                return false;
+            }
+        }
     }
 #endif
 

+ 11 - 0
core/iwasm/compilation/aot_compiler.h

@@ -532,8 +532,13 @@ set_local_gc_ref(AOTCompFrame *frame, int n, LLVMValueRef value, uint8 ref_type)
 #define IS_MEMORY64 (comp_ctx->comp_data->memories[0].flags & MEMORY64_FLAG)
 #define MEMORY64_COND_VALUE(VAL_IF_ENABLED, VAL_IF_DISABLED) \
     (IS_MEMORY64 ? VAL_IF_ENABLED : VAL_IF_DISABLED)
+#define IS_TABLE64(i) \
+    (comp_ctx->comp_data->tables[i].table_type.flags & TABLE64_FLAG)
+#define TABLE64_COND_VALUE(i, VAL_IF_ENABLED, VAL_IF_DISABLED) \
+    (IS_TABLE64(i) ? VAL_IF_ENABLED : VAL_IF_DISABLED)
 #else
 #define MEMORY64_COND_VALUE(VAL_IF_ENABLED, VAL_IF_DISABLED) (VAL_IF_DISABLED)
+#define TABLE64_COND_VALUE(i, VAL_IF_ENABLED, VAL_IF_DISABLED) (VAL_IF_DISABLED)
 #endif
 
 #define POP_I32(v) POP(v, VALUE_TYPE_I32)
@@ -548,6 +553,9 @@ set_local_gc_ref(AOTCompFrame *frame, int n, LLVMValueRef value, uint8 ref_type)
     POP(v, MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32))
 #define POP_PAGE_COUNT(v) \
     POP(v, MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32))
+#define POP_TBL_ELEM_IDX(v) \
+    POP(v, TABLE64_COND_VALUE(tbl_idx, VALUE_TYPE_I64, VALUE_TYPE_I32))
+#define POP_TBL_ELEM_LEN(v) POP_TBL_ELEM_IDX(v)
 
 #define POP_COND(llvm_value)                                                   \
     do {                                                                       \
@@ -613,6 +621,9 @@ set_local_gc_ref(AOTCompFrame *frame, int n, LLVMValueRef value, uint8 ref_type)
 #define PUSH_GC_REF(v) PUSH(v, VALUE_TYPE_GC_REF)
 #define PUSH_PAGE_COUNT(v) \
     PUSH(v, MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32))
+#define PUSH_TBL_ELEM_IDX(v) \
+    PUSH(v, TABLE64_COND_VALUE(tbl_idx, VALUE_TYPE_I64, VALUE_TYPE_I32))
+#define PUSH_TBL_ELEM_LEN(v) PUSH_TBL_ELEM_IDX(v)
 
 #define SET_CONST(v)                                                          \
     do {                                                                      \

+ 38 - 2
core/iwasm/compilation/aot_emit_function.c

@@ -2089,6 +2089,9 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     LLVMValueRef ext_ret_offset, ext_ret_ptr, ext_ret, res;
     LLVMValueRef *param_values = NULL, *value_rets = NULL;
     LLVMValueRef *result_phis = NULL, value_ret, import_func_count;
+#if WASM_ENABLE_MEMORY64 != 0
+    LLVMValueRef u32_max, u32_cmp_result;
+#endif
     LLVMTypeRef *param_types = NULL, ret_type;
     LLVMTypeRef llvm_func_type, llvm_func_ptr_type;
     LLVMTypeRef ext_ret_ptr_type;
@@ -2153,7 +2156,7 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     func_param_count = func_type->param_count;
     func_result_count = func_type->result_count;
 
-    POP_I32(elem_idx);
+    POP_TBL_ELEM_IDX(elem_idx);
 
     /* get the cur size of the table instance */
     if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx)
@@ -2182,6 +2185,27 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
+#if WASM_ENABLE_MEMORY64 != 0
+    /* Check if elem index >= UINT32_MAX */
+    if (IS_TABLE64(tbl_idx)) {
+        if (!(u32_max = I64_CONST(UINT32_MAX))) {
+            aot_set_last_error("llvm build const failed");
+            goto fail;
+        }
+        if (!(u32_cmp_result =
+                  LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx,
+                                u32_max, "cmp_elem_idx_u32_max"))) {
+            aot_set_last_error("llvm build icmp failed.");
+            goto fail;
+        }
+        if (!(elem_idx = LLVMBuildTrunc(comp_ctx->builder, elem_idx, I32_TYPE,
+                                        "elem_idx_i32"))) {
+            aot_set_last_error("llvm build trunc failed.");
+            goto fail;
+        }
+    }
+#endif
+
     /* Check if (uint32)elem index >= table size */
     if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx,
                                        table_size_const, "cmp_elem_idx"))) {
@@ -2189,7 +2213,19 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
-    /* Throw exception if elem index >= table size */
+#if WASM_ENABLE_MEMORY64 != 0
+    if (IS_TABLE64(tbl_idx)) {
+        if (!(cmp_elem_idx =
+                  LLVMBuildOr(comp_ctx->builder, cmp_elem_idx, u32_cmp_result,
+                              "larger_than_u32_max_or_cur_size"))) {
+            aot_set_last_error("llvm build or failed.");
+            goto fail;
+        }
+    }
+#endif
+
+    /* Throw exception if elem index >= table size or elem index >= UINT32_MAX
+     */
     if (!(check_elem_idx_succ = LLVMAppendBasicBlockInContext(
               comp_ctx->context, func_ctx->func, "check_elem_idx_succ"))) {
         aot_set_last_error("llvm add basic block failed.");

+ 134 - 11
core/iwasm/compilation/aot_emit_table.c

@@ -10,6 +10,72 @@
 #include "aot_emit_gc.h"
 #endif
 
+static bool
+zero_extend_u64(AOTCompContext *comp_ctx, LLVMValueRef *value, const char *name)
+{
+    if (comp_ctx->pointer_size == sizeof(uint64)) {
+        /* zero extend to uint64 if the target is 64-bit */
+        *value = LLVMBuildZExt(comp_ctx->builder, *value, I64_TYPE, name);
+        if (!*value) {
+            aot_set_last_error("llvm build zero extend failed.");
+            return false;
+        }
+    }
+    return true;
+}
+
+/* check whether a table64 elem idx is greater than UINT32_MAX, if so, throw
+ * exception, otherwise trunc it to uint32 */
+static bool
+check_tbl_elem_idx_and_trunc(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                             LLVMValueRef *elem_idx, uint32 tbl_idx)
+{
+    LLVMValueRef u32_max, u32_cmp_result;
+    LLVMBasicBlockRef check_elem_idx_succ;
+
+#if WASM_ENABLE_MEMORY64 != 0
+    if (!IS_TABLE64(tbl_idx)) {
+        return true;
+    }
+
+    /* Check if elem index >= UINT32_MAX */
+    if (!(u32_max = I64_CONST(UINT32_MAX))) {
+        aot_set_last_error("llvm build const failed");
+        goto fail;
+    }
+    if (!(u32_cmp_result =
+              LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, *elem_idx, u32_max,
+                            "cmp_elem_idx_u32_max"))) {
+        aot_set_last_error("llvm build icmp failed.");
+        goto fail;
+    }
+    if (!(*elem_idx = LLVMBuildTrunc(comp_ctx->builder, *elem_idx, I32_TYPE,
+                                     "elem_idx_i32"))) {
+        aot_set_last_error("llvm build trunc failed.");
+        goto fail;
+    }
+
+    /* Throw exception if elem index >= UINT32_MAX*/
+    if (!(check_elem_idx_succ = LLVMAppendBasicBlockInContext(
+              comp_ctx->context, func_ctx->func, "check_elem_idx_succ"))) {
+        aot_set_last_error("llvm add basic block failed.");
+        goto fail;
+    }
+
+    LLVMMoveBasicBlockAfter(check_elem_idx_succ,
+                            LLVMGetInsertBlock(comp_ctx->builder));
+
+    if (!(aot_emit_exception(comp_ctx, func_ctx,
+                             EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, true,
+                             u32_cmp_result, check_elem_idx_succ)))
+        goto fail;
+#endif
+
+    return true;
+fail:
+    return false;
+}
+
 uint64
 get_tbl_inst_offset(const AOTCompContext *comp_ctx,
                     const AOTFuncContext *func_ctx, uint32 tbl_idx)
@@ -158,6 +224,10 @@ aot_check_table_access(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &elem_idx, tbl_idx)) {
+        goto fail;
+    }
+
     /* Check if (uint32)elem index >= table size */
     if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx,
                                        tbl_sz, "cmp_elem_idx"))) {
@@ -192,7 +262,7 @@ aot_compile_op_table_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     LLVMValueRef elem_idx, offset, func_idx;
     LLVMValueRef table_elem_base, table_elem_addr, table_elem;
 
-    POP_I32(elem_idx);
+    POP_TBL_ELEM_IDX(elem_idx);
 
     if (!aot_check_table_access(comp_ctx, func_ctx, tbl_idx, elem_idx)) {
         goto fail;
@@ -289,7 +359,7 @@ aot_compile_op_table_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         }
     }
 
-    POP_I32(elem_idx);
+    POP_TBL_ELEM_IDX(elem_idx);
 
     if (!aot_check_table_access(comp_ctx, func_ctx, tbl_idx, elem_idx)) {
         goto fail;
@@ -388,7 +458,11 @@ aot_compile_op_table_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     /* s */
     POP_I32(param_values[4]);
     /* d */
-    POP_I32(param_values[5]);
+    POP_TBL_ELEM_IDX(param_values[5]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[5],
+                                      tbl_idx)) {
+        goto fail;
+    }
 
     /* "" means return void */
     if (!(LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 6,
@@ -408,6 +482,7 @@ aot_compile_op_table_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 {
     LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type;
     LLVMValueRef func, param_values[6], value;
+    uint32 tbl_idx;
 
     param_types[0] = INT8_PTR_TYPE;
     param_types[1] = I32_TYPE;
@@ -434,12 +509,34 @@ aot_compile_op_table_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
+    /* In table64, the length should be i32 type if any one of src/dst table
+     * is i32 type, set the table index to the lesser-or-equal table when
+     * popping length n */
+    if (!(comp_ctx->comp_data->tables[src_tbl_idx].table_type.flags
+          & TABLE64_FLAG))
+        tbl_idx = src_tbl_idx;
+    else
+        tbl_idx = dst_tbl_idx;
     /* n */
-    POP_I32(param_values[3]);
+    POP_TBL_ELEM_LEN(param_values[3]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[3],
+                                      tbl_idx)) {
+        goto fail;
+    }
     /* s */
-    POP_I32(param_values[4]);
+    tbl_idx = src_tbl_idx;
+    POP_TBL_ELEM_IDX(param_values[4]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[4],
+                                      tbl_idx)) {
+        goto fail;
+    }
     /* d */
-    POP_I32(param_values[5]);
+    tbl_idx = dst_tbl_idx;
+    POP_TBL_ELEM_IDX(param_values[5]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[5],
+                                      tbl_idx)) {
+        goto fail;
+    }
 
     /* "" means return void */
     if (!(LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 6,
@@ -484,7 +581,14 @@ aot_compile_op_table_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
-    PUSH_I32(tbl_sz);
+#if WASM_ENABLE_MEMORY64 != 0
+    if (IS_TABLE64(tbl_idx)) {
+        if (!zero_extend_u64(comp_ctx, &tbl_sz, "length64")) {
+            goto fail;
+        }
+    }
+#endif
+    PUSH_TBL_ELEM_IDX(tbl_sz);
 
     return true;
 fail:
@@ -517,7 +621,11 @@ aot_compile_op_table_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     }
 
     /* n */
-    POP_I32(param_values[2]);
+    POP_TBL_ELEM_LEN(param_values[2]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[2],
+                                      tbl_idx)) {
+        goto fail;
+    }
     /* v */
 
     if (comp_ctx->enable_gc) {
@@ -545,7 +653,14 @@ aot_compile_op_table_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         goto fail;
     }
 
-    PUSH_I32(ret);
+#if WASM_ENABLE_MEMORY64 != 0
+    if (IS_TABLE64(tbl_idx)) {
+        if (!zero_extend_u64(comp_ctx, &ret, "table_size64")) {
+            goto fail;
+        }
+    }
+#endif
+    PUSH_TBL_ELEM_LEN(ret);
 
     return true;
 fail:
@@ -579,7 +694,11 @@ aot_compile_op_table_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     }
 
     /* n */
-    POP_I32(param_values[2]);
+    POP_TBL_ELEM_LEN(param_values[2]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[2],
+                                      tbl_idx)) {
+        goto fail;
+    }
     /* v */
 
     if (comp_ctx->enable_gc) {
@@ -601,7 +720,11 @@ aot_compile_op_table_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         }
     }
     /* i */
-    POP_I32(param_values[4]);
+    POP_TBL_ELEM_IDX(param_values[4]);
+    if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &param_values[4],
+                                      tbl_idx)) {
+        goto fail;
+    }
 
     /* "" means return void */
     if (!(LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 5,

+ 6 - 0
core/iwasm/interpreter/wasm.h

@@ -93,6 +93,10 @@ extern "C" {
 #define MAX_PAGE_COUNT_FLAG 0x01
 #define SHARED_MEMORY_FLAG 0x02
 #define MEMORY64_FLAG 0x04
+#define MAX_TABLE_SIZE_FLAG 0x01
+/* the shared flag for table is not actual used now */
+#define SHARED_TABLE_FLAG 0x02
+#define TABLE64_FLAG 0x04
 
 /**
  * In the multi-memory proposal, the memarg in loads and stores are
@@ -494,6 +498,7 @@ typedef struct WASMTableType {
      * 0: no max size and not shared
      * 1: has max size
      * 2: shared
+     * 4: table64
      */
     uint8 flags;
     bool possible_grow;
@@ -520,6 +525,7 @@ typedef uint64 mem_offset_t;
 typedef uint32 mem_offset_t;
 #define PR_MEM_OFFSET PRIu32
 #endif
+typedef mem_offset_t tbl_elem_idx_t;
 
 typedef struct WASMMemory {
     uint32 flags;

+ 106 - 36
core/iwasm/interpreter/wasm_interp_classic.c

@@ -511,9 +511,9 @@ wasm_interp_get_frame_ref(WASMInterpFrame *frame)
 #endif
 
 #if WASM_ENABLE_MEMORY64 != 0
-#define PUSH_MEM_OFFSET(value)                     \
+#define COND_PUSH_TEMPLATE(cond, value)            \
     do {                                           \
-        if (is_memory64) {                         \
+        if (cond) {                                \
             PUT_I64_TO_ADDR(frame_sp, value);      \
             frame_sp += 2;                         \
         }                                          \
@@ -521,8 +521,11 @@ wasm_interp_get_frame_ref(WASMInterpFrame *frame)
             *(int32 *)frame_sp++ = (int32)(value); \
         }                                          \
     } while (0)
+#define PUSH_MEM_OFFSET(value) COND_PUSH_TEMPLATE(is_memory64, value)
+#define PUSH_TBL_ELEM_IDX(value) COND_PUSH_TEMPLATE(is_table64, value)
 #else
 #define PUSH_MEM_OFFSET(value) PUSH_I32(value)
+#define PUSH_TBL_ELEM_IDX(value) PUSH_I32(value)
 #endif
 
 #define PUSH_PAGE_COUNT(value) PUSH_MEM_OFFSET(value)
@@ -558,8 +561,10 @@ wasm_interp_get_frame_ref(WASMInterpFrame *frame)
 
 #if WASM_ENABLE_MEMORY64 != 0
 #define POP_MEM_OFFSET() (is_memory64 ? POP_I64() : POP_I32())
+#define POP_TBL_ELEM_IDX() (is_table64 ? POP_I64() : POP_I32())
 #else
 #define POP_MEM_OFFSET() POP_I32()
+#define POP_TBL_ELEM_IDX() POP_I32()
 #endif
 
 #define POP_PAGE_COUNT() POP_MEM_OFFSET()
@@ -1562,7 +1567,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
     uint8 opcode;
     uint32 i, depth, cond, count, fidx, tidx, lidx, frame_size = 0;
     uint32 all_cell_num = 0;
-    int32 val;
+    tbl_elem_idx_t val;
     uint8 *else_addr, *end_addr, *maddr = NULL;
     uint32 local_idx, local_offset, global_idx;
     uint8 local_type, *global_addr;
@@ -1602,6 +1607,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
     /* TODO: multi-memories for now assuming the memory idx type is consistent
      * across multi-memories */
     bool is_memory64 = false;
+    bool is_table64 = false;
     if (memory)
         is_memory64 = memory->is_memory64;
 #endif
@@ -2315,7 +2321,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
                 /**
                  * type check. compiler will make sure all like
-                 * (call_indirect (type $x) (i32.const 1))
+                 * (call_indirect (type $x) (it.const 1))
                  * the function type has to be defined in the module also
                  * no matter it is used or not
                  */
@@ -2334,9 +2340,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 /* clang-format on */
 
                 tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                is_table64 = tbl_inst->is_table64;
+#endif
 
-                val = POP_I32();
-                if ((uint32)val >= tbl_inst->cur_size) {
+                val = POP_TBL_ELEM_IDX();
+                if (val >= tbl_inst->cur_size) {
                     wasm_set_exception(module, "undefined element");
                     goto got_exception;
                 }
@@ -2482,15 +2491,19 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
             HANDLE_OP(WASM_OP_TABLE_GET)
             {
-                uint32 tbl_idx, elem_idx;
+                uint32 tbl_idx;
+                tbl_elem_idx_t elem_idx;
                 WASMTableInstance *tbl_inst;
 
                 read_leb_uint32(frame_ip, frame_ip_end, tbl_idx);
                 bh_assert(tbl_idx < module->table_count);
 
                 tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                is_table64 = tbl_inst->is_table64;
+#endif
 
-                elem_idx = POP_I32();
+                elem_idx = POP_TBL_ELEM_IDX();
                 if (elem_idx >= tbl_inst->cur_size) {
                     wasm_set_exception(module, "out of bounds table access");
                     goto got_exception;
@@ -2507,20 +2520,24 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
             HANDLE_OP(WASM_OP_TABLE_SET)
             {
                 WASMTableInstance *tbl_inst;
-                uint32 tbl_idx, elem_idx;
+                uint32 tbl_idx;
+                tbl_elem_idx_t elem_idx;
                 table_elem_type_t elem_val;
 
                 read_leb_uint32(frame_ip, frame_ip_end, tbl_idx);
                 bh_assert(tbl_idx < module->table_count);
 
                 tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                is_table64 = tbl_inst->is_table64;
+#endif
 
 #if WASM_ENABLE_GC == 0
                 elem_val = POP_I32();
 #else
                 elem_val = POP_REF();
 #endif
-                elem_idx = POP_I32();
+                elem_idx = POP_TBL_ELEM_IDX();
                 if (elem_idx >= tbl_inst->cur_size) {
                     wasm_set_exception(module, "out of bounds table access");
                     goto got_exception;
@@ -4616,13 +4633,19 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
             HANDLE_OP(WASM_OP_MEMORY_GROW)
             {
-                uint32 mem_idx, delta, prev_page_count;
+                uint32 mem_idx, prev_page_count;
+                mem_offset_t delta;
 
                 read_leb_memidx(frame_ip, frame_ip_end, mem_idx);
                 prev_page_count = memory->cur_page_count;
-                delta = (uint32)POP_PAGE_COUNT();
+                delta = POP_PAGE_COUNT();
 
-                if (!wasm_enlarge_memory_with_idx(module, delta, mem_idx)) {
+                if (
+#if WASM_ENABLE_MEMORY64 != 0
+                    delta > UINT32_MAX ||
+#endif
+                    !wasm_enlarge_memory_with_idx(module, (uint32)delta,
+                                                  mem_idx)) {
                     /* failed to memory.grow, return -1 */
                     PUSH_PAGE_COUNT(-1);
                 }
@@ -5778,8 +5801,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
                     case WASM_OP_TABLE_INIT:
                     {
-                        uint32 tbl_idx, elem_idx;
-                        uint32 n, s, d;
+                        uint32 tbl_idx;
+                        tbl_elem_idx_t elem_idx, d;
+                        uint32 n, s;
                         WASMTableInstance *tbl_inst;
                         table_elem_type_t *table_elems;
                         InitializerExpression *tbl_seg_init_values = NULL,
@@ -5793,10 +5817,13 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                         bh_assert(tbl_idx < module->module->table_count);
 
                         tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = tbl_inst->is_table64;
+#endif
 
                         n = (uint32)POP_I32();
                         s = (uint32)POP_I32();
-                        d = (uint32)POP_I32();
+                        d = (tbl_elem_idx_t)POP_TBL_ELEM_IDX();
 
                         if (!bh_bitmap_get_bit(module->e->common.elem_dropped,
                                                elem_idx)) {
@@ -5809,8 +5836,15 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                                     .value_count;
                         }
 
-                        if (offset_len_out_of_bounds(s, n, tbl_seg_len)
-                            || offset_len_out_of_bounds(d, n,
+                        /* TODO: memory64 current implementation of table64
+                         * still assumes the max table size UINT32_MAX
+                         */
+                        if (
+#if WASM_ENABLE_MEMORY64 != 0
+                            d > UINT32_MAX ||
+#endif
+                            offset_len_out_of_bounds(s, n, tbl_seg_len)
+                            || offset_len_out_of_bounds((uint32)d, n,
                                                         tbl_inst->cur_size)) {
                             wasm_set_exception(module,
                                                "out of bounds table access");
@@ -5864,7 +5898,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     case WASM_OP_TABLE_COPY:
                     {
                         uint32 src_tbl_idx, dst_tbl_idx;
-                        uint32 n, s, d;
+                        tbl_elem_idx_t n, s, d;
                         WASMTableInstance *src_tbl_inst, *dst_tbl_inst;
 
                         read_leb_uint32(frame_ip, frame_ip_end, dst_tbl_idx);
@@ -5877,14 +5911,29 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
                         src_tbl_inst = wasm_get_table_inst(module, src_tbl_idx);
 
-                        n = (uint32)POP_I32();
-                        s = (uint32)POP_I32();
-                        d = (uint32)POP_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = src_tbl_inst->is_table64
+                                     && dst_tbl_inst->is_table64;
+#endif
+                        n = (tbl_elem_idx_t)POP_TBL_ELEM_IDX();
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = src_tbl_inst->is_table64;
+#endif
+                        s = (tbl_elem_idx_t)POP_TBL_ELEM_IDX();
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = dst_tbl_inst->is_table64;
+#endif
+                        d = (tbl_elem_idx_t)POP_TBL_ELEM_IDX();
 
-                        if (offset_len_out_of_bounds(d, n,
+                        if (
+#if WASM_ENABLE_MEMORY64 != 0
+                            n > UINT32_MAX || s > UINT32_MAX || d > UINT32_MAX
+                            ||
+#endif
+                            offset_len_out_of_bounds((uint32)d, (uint32)n,
                                                      dst_tbl_inst->cur_size)
                             || offset_len_out_of_bounds(
-                                s, n, src_tbl_inst->cur_size)) {
+                                (uint32)s, (uint32)n, src_tbl_inst->cur_size)) {
                             wasm_set_exception(module,
                                                "out of bounds table access");
                             goto got_exception;
@@ -5907,28 +5956,37 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     case WASM_OP_TABLE_GROW:
                     {
                         WASMTableInstance *tbl_inst;
-                        uint32 tbl_idx, n, orig_tbl_sz;
+                        uint32 tbl_idx, orig_tbl_sz;
+                        tbl_elem_idx_t n;
                         table_elem_type_t init_val;
 
                         read_leb_uint32(frame_ip, frame_ip_end, tbl_idx);
                         bh_assert(tbl_idx < module->table_count);
 
                         tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = tbl_inst->is_table64;
+#endif
 
                         orig_tbl_sz = tbl_inst->cur_size;
 
-                        n = POP_I32();
+                        n = POP_TBL_ELEM_IDX();
 #if WASM_ENABLE_GC == 0
                         init_val = POP_I32();
 #else
                         init_val = POP_REF();
 #endif
 
-                        if (!wasm_enlarge_table(module, tbl_idx, n, init_val)) {
-                            PUSH_I32(-1);
+                        if (
+#if WASM_ENABLE_MEMORY64 != 0
+                            n > UINT32_MAX ||
+#endif
+                            !wasm_enlarge_table(module, tbl_idx, (uint32)n,
+                                                init_val)) {
+                            PUSH_TBL_ELEM_IDX(-1);
                         }
                         else {
-                            PUSH_I32(orig_tbl_sz);
+                            PUSH_TBL_ELEM_IDX(orig_tbl_sz);
                         }
                         break;
                     }
@@ -5941,13 +5999,17 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                         bh_assert(tbl_idx < module->table_count);
 
                         tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = tbl_inst->is_table64;
+#endif
 
-                        PUSH_I32(tbl_inst->cur_size);
+                        PUSH_TBL_ELEM_IDX(tbl_inst->cur_size);
                         break;
                     }
                     case WASM_OP_TABLE_FILL:
                     {
-                        uint32 tbl_idx, n;
+                        uint32 tbl_idx;
+                        tbl_elem_idx_t n, elem_idx;
                         WASMTableInstance *tbl_inst;
                         table_elem_type_t fill_val;
 
@@ -5955,24 +6017,32 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                         bh_assert(tbl_idx < module->table_count);
 
                         tbl_inst = wasm_get_table_inst(module, tbl_idx);
+#if WASM_ENABLE_MEMORY64 != 0
+                        is_table64 = tbl_inst->is_table64;
+#endif
 
-                        n = POP_I32();
+                        n = POP_TBL_ELEM_IDX();
 #if WASM_ENABLE_GC == 0
                         fill_val = POP_I32();
 #else
                         fill_val = POP_REF();
 #endif
-                        i = POP_I32();
+                        elem_idx = POP_TBL_ELEM_IDX();
 
-                        if (offset_len_out_of_bounds(i, n,
+                        if (
+#if WASM_ENABLE_MEMORY64 != 0
+                            n > UINT32_MAX || elem_idx > UINT32_MAX ||
+#endif
+                            offset_len_out_of_bounds((uint32)elem_idx,
+                                                     (uint32)n,
                                                      tbl_inst->cur_size)) {
                             wasm_set_exception(module,
                                                "out of bounds table access");
                             goto got_exception;
                         }
 
-                        for (; n != 0; i++, n--) {
-                            tbl_inst->elems[i] = fill_val;
+                        for (; n != 0; elem_idx++, n--) {
+                            tbl_inst->elems[elem_idx] = fill_val;
                         }
                         break;
                     }

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

@@ -51,6 +51,18 @@ has_module_memory64(WASMModule *module)
 
     return false;
 }
+
+static bool
+is_table_64bit(WASMModule *module, uint32 table_idx)
+{
+    if (table_idx < module->import_table_count)
+        return !!(module->import_tables[table_idx].u.table.table_type.flags
+                  & TABLE64_FLAG);
+    else
+        return !!(module->tables[table_idx].table_type.flags & TABLE64_FLAG);
+
+    return false;
+}
 #endif
 
 static void
@@ -2201,10 +2213,14 @@ fail:
 }
 
 static void
-adjust_table_max_size(uint32 init_size, uint32 max_size_flag, uint32 *max_size)
+adjust_table_max_size(bool is_table64, uint32 init_size, uint32 max_size_flag,
+                      uint32 *max_size)
 {
     uint32 default_max_size;
 
+    /* TODO: current still use UINT32_MAX as upper limit for table size to keep
+     * ABI unchanged */
+    (void)is_table64;
     if (UINT32_MAX / 2 > init_size)
         default_max_size = init_size * 2;
     else
@@ -2499,9 +2515,9 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
                   const char *table_name, WASMTableImport *table,
                   char *error_buf, uint32 error_buf_size)
 {
-    const uint8 *p = *p_buf, *p_end = buf_end;
-    uint32 declare_elem_type = 0, declare_max_size_flag = 0,
-           declare_init_size = 0, declare_max_size = 0;
+    const uint8 *p = *p_buf, *p_end = buf_end, *p_org;
+    uint32 declare_elem_type = 0, table_flag = 0, declare_init_size = 0,
+           declare_max_size = 0;
 #if WASM_ENABLE_MULTI_MODULE != 0
     WASMModule *sub_module = NULL;
     WASMTable *linked_table = NULL;
@@ -2510,6 +2526,7 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
     WASMRefType ref_type;
     bool need_ref_type_map;
 #endif
+    bool is_table64 = false;
 
 #if WASM_ENABLE_GC == 0
     CHECK_BUF(p, p_end, 1);
@@ -2548,23 +2565,29 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
 #endif
 #endif /* end of WASM_ENABLE_GC == 0 */
 
-    read_leb_uint32(p, p_end, declare_max_size_flag);
-    if (declare_max_size_flag > 1) {
-        set_error_buf(error_buf, error_buf_size, "integer too large");
+    p_org = p;
+    read_leb_uint32(p, p_end, table_flag);
+    is_table64 = table_flag & TABLE64_FLAG;
+    if (p - p_org > 1) {
+        LOG_VERBOSE("integer representation too long(import table)");
+        set_error_buf(error_buf, error_buf_size, "invalid limits flags");
         return false;
     }
 
-    read_leb_uint32(p, p_end, declare_init_size);
+    if (!wasm_table_check_flags(table_flag, error_buf, error_buf_size, false)) {
+        return false;
+    }
 
-    if (declare_max_size_flag) {
+    read_leb_uint32(p, p_end, declare_init_size);
+    if (table_flag & MAX_TABLE_SIZE_FLAG) {
         read_leb_uint32(p, p_end, declare_max_size);
         if (!check_table_max_size(declare_init_size, declare_max_size,
                                   error_buf, error_buf_size))
             return false;
     }
 
-    adjust_table_max_size(declare_init_size, declare_max_size_flag,
-                          &declare_max_size);
+    adjust_table_max_size(is_table64, declare_init_size,
+                          table_flag & MAX_TABLE_SIZE_FLAG, &declare_max_size);
 
     *p_buf = p;
 
@@ -2582,7 +2605,7 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
                 declare_elem_type = linked_table->table_type.elem_type;
                 declare_init_size = linked_table->table_type.init_size;
                 declare_max_size = linked_table->table_type.max_size;
-                declare_max_size_flag = linked_table->table_type.flags;
+                table_flag = linked_table->table_type.flags;
                 table->import_table_linked = linked_table;
                 table->import_module = sub_module;
             }
@@ -2591,12 +2614,17 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
 #endif /* WASM_ENABLE_MULTI_MODULE != 0 */
 
     /* (table (export "table") 10 20 funcref) */
+    /* (table (export "table64") 10 20 funcref) */
     /* we need this section working in wamrc */
     if (!strcmp("spectest", sub_module_name)) {
         const uint32 spectest_table_init_size = 10;
         const uint32 spectest_table_max_size = 20;
 
-        if (strcmp("table", table_name)) {
+        if (strcmp("table", table_name)
+#if WASM_ENABLE_MEMORY64 != 0
+            && strcmp("table64", table_name)
+#endif
+        ) {
             set_error_buf(error_buf, error_buf_size,
                           "incompatible import type or unknown import");
             return false;
@@ -2616,7 +2644,7 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
     /* now we believe all declaration are ok */
     table->table_type.elem_type = declare_elem_type;
     table->table_type.init_size = declare_init_size;
-    table->table_type.flags = declare_max_size_flag;
+    table->table_type.flags = table_flag;
     table->table_type.max_size = declare_max_size;
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
@@ -2709,7 +2737,7 @@ load_memory_import(const uint8 **p_buf, const uint8 *buf_end,
     read_leb_uint32(p, p_end, mem_flag);
     is_memory64 = mem_flag & MEMORY64_FLAG;
     if (p - p_org > 1) {
-        LOG_VERBOSE("integer representation too long");
+        LOG_VERBOSE("integer representation too long(import memory)");
         set_error_buf(error_buf, error_buf_size, "invalid limits flags");
         return false;
     }
@@ -3024,6 +3052,7 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
     WASMRefType ref_type;
     bool need_ref_type_map;
 #endif
+    bool is_table64 = false;
 
 #if WASM_ENABLE_GC == 0
     CHECK_BUF(p, p_end, 1);
@@ -3061,34 +3090,20 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
 
     p_org = p;
     read_leb_uint32(p, p_end, table->table_type.flags);
-#if WASM_ENABLE_SHARED_MEMORY == 0
-    if (p - p_org > 1) {
-        set_error_buf(error_buf, error_buf_size,
-                      "integer representation too long");
-        return false;
-    }
-    if (table->table_type.flags > 1) {
-        set_error_buf(error_buf, error_buf_size, "integer too large");
-        return false;
-    }
-#else
+    is_table64 = table->table_type.flags & TABLE64_FLAG;
     if (p - p_org > 1) {
+        LOG_VERBOSE("integer representation too long(table)");
         set_error_buf(error_buf, error_buf_size, "invalid limits flags");
         return false;
     }
-    if (table->table_type.flags == 2) {
-        set_error_buf(error_buf, error_buf_size, "tables cannot be shared");
-        return false;
-    }
-    if (table->table_type.flags > 1) {
-        set_error_buf(error_buf, error_buf_size, "invalid limits flags");
+
+    if (!wasm_table_check_flags(table->table_type.flags, error_buf,
+                                error_buf_size, false)) {
         return false;
     }
-#endif
 
     read_leb_uint32(p, p_end, table->table_type.init_size);
-
-    if (table->table_type.flags) {
+    if (table->table_type.flags & MAX_TABLE_SIZE_FLAG) {
         read_leb_uint32(p, p_end, table->table_type.max_size);
         if (!check_table_max_size(table->table_type.init_size,
                                   table->table_type.max_size, error_buf,
@@ -3096,7 +3111,8 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
             return false;
     }
 
-    adjust_table_max_size(table->table_type.init_size, table->table_type.flags,
+    adjust_table_max_size(is_table64, table->table_type.init_size,
+                          table->table_type.flags & MAX_TABLE_SIZE_FLAG,
                           &table->table_type.max_size);
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
@@ -3128,7 +3144,7 @@ load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory,
     read_leb_uint32(p, p_end, memory->flags);
     is_memory64 = memory->flags & MEMORY64_FLAG;
     if (p - p_org > 1) {
-        LOG_VERBOSE("integer representation too long");
+        LOG_VERBOSE("integer representation too long(memory)");
         set_error_buf(error_buf, error_buf_size, "invalid limits flags");
         return false;
     }
@@ -4402,6 +4418,7 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                            uint32 error_buf_size)
 {
     const uint8 *p = buf, *p_end = buf_end;
+    uint8 table_elem_idx_type;
     uint32 table_segment_count, i;
     uint64 total_size;
     WASMTableSeg *table_segment;
@@ -4424,6 +4441,7 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                               "invalid elements segment kind");
                 return false;
             }
+            table_elem_idx_type = VALUE_TYPE_I32;
 
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
             read_leb_uint32(p, p_end, table_segment->mode);
@@ -4459,9 +4477,17 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                     if (!check_table_index(module, table_segment->table_index,
                                            error_buf, error_buf_size))
                         return false;
-                    if (!load_init_expr(
-                            module, &p, p_end, &table_segment->base_offset,
-                            VALUE_TYPE_I32, NULL, error_buf, error_buf_size))
+
+#if WASM_ENABLE_MEMORY64 != 0
+                    table_elem_idx_type =
+                        is_table_64bit(module, table_segment->table_index)
+                            ? VALUE_TYPE_I64
+                            : VALUE_TYPE_I32;
+#endif
+                    if (!load_init_expr(module, &p, p_end,
+                                        &table_segment->base_offset,
+                                        table_elem_idx_type, NULL, error_buf,
+                                        error_buf_size))
                         return false;
 
                     if (table_segment->mode == 0) {
@@ -4509,9 +4535,16 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                                           &table_segment->table_index,
                                           error_buf, error_buf_size))
                         return false;
-                    if (!load_init_expr(
-                            module, &p, p_end, &table_segment->base_offset,
-                            VALUE_TYPE_I32, NULL, error_buf, error_buf_size))
+#if WASM_ENABLE_MEMORY64 != 0
+                    table_elem_idx_type =
+                        is_table_64bit(module, table_segment->table_index)
+                            ? VALUE_TYPE_I64
+                            : VALUE_TYPE_I32;
+#endif
+                    if (!load_init_expr(module, &p, p_end,
+                                        &table_segment->base_offset,
+                                        table_elem_idx_type, NULL, error_buf,
+                                        error_buf_size))
                         return false;
                     if (!load_elem_type(module, &p, p_end,
                                         &table_segment->elem_type,
@@ -4563,7 +4596,7 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                                   "unknown element segment kind");
                     return false;
             }
-#else  /* else of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */
+#else /* else of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */
             /*
              * like:      00  41 05 0b               04 00 01 00 01
              * for: (elem 0   (offset (i32.const 5)) $f1 $f2 $f1 $f2)
@@ -4572,8 +4605,14 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                                   &table_segment->table_index, error_buf,
                                   error_buf_size))
                 return false;
+#if WASM_ENABLE_MEMORY64 != 0
+            table_elem_idx_type =
+                is_table_64bit(module, table_segment->table_index)
+                    ? VALUE_TYPE_I64
+                    : VALUE_TYPE_I32;
+#endif
             if (!load_init_expr(module, &p, p_end, &table_segment->base_offset,
-                                VALUE_TYPE_I32, NULL, error_buf,
+                                table_elem_idx_type, NULL, error_buf,
                                 error_buf_size))
                 return false;
             if (!load_func_index_vec(&p, p_end, module, table_segment,
@@ -4588,6 +4627,16 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                 return false;
 #endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */
 
+#if WASM_ENABLE_MEMORY64 != 0
+            if (table_elem_idx_type == VALUE_TYPE_I64
+                && table_segment->base_offset.u.u64 > UINT32_MAX) {
+                set_error_buf(error_buf, error_buf_size,
+                              "In table64, table base offset can't be "
+                              "larger than UINT32_MAX");
+                return false;
+            }
+#endif
+
 #if WASM_ENABLE_WAMR_COMPILER != 0
             if (table_segment->elem_type == VALUE_TYPE_EXTERNREF)
                 module->is_ref_types_used = true;
@@ -6110,6 +6159,12 @@ load_from_sections(WASMModule *module, WASMSection *sections,
 #endif
     }
 
+#if WASM_ENABLE_MEMORY64 != 0
+    if (!check_memory64_flags_consistency(module, error_buf, error_buf_size,
+                                          false))
+        return false;
+#endif
+
     calculate_global_data_offset(module);
 
 #if WASM_ENABLE_FAST_JIT != 0
@@ -9608,6 +9663,7 @@ fail:
 #define POP_REF(Type) TEMPLATE_POP_REF(Type)
 #define PUSH_MEM_OFFSET() TEMPLATE_PUSH_REF(mem_offset_type)
 #define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET()
+#define PUSH_TBL_ELEM_IDX() TEMPLATE_PUSH_REF(table_elem_idx_type)
 
 #define POP_I32() TEMPLATE_POP(I32)
 #define POP_F32() TEMPLATE_POP(F32)
@@ -9618,6 +9674,7 @@ fail:
 #define POP_EXTERNREF() TEMPLATE_POP(EXTERNREF)
 #define POP_STRINGREF() TEMPLATE_POP(STRINGREF)
 #define POP_MEM_OFFSET() TEMPLATE_POP_REF(mem_offset_type)
+#define POP_TBL_ELEM_IDX() TEMPLATE_POP_REF(table_elem_idx_type)
 
 #if WASM_ENABLE_FAST_INTERP != 0
 
@@ -10803,7 +10860,8 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
 {
     uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org;
     uint32 param_count, local_count, global_count;
-    uint8 *param_types, *local_types, local_type, global_type, mem_offset_type;
+    uint8 *param_types, *local_types, local_type, global_type, mem_offset_type,
+        table_elem_idx_type;
     BlockType func_block_type;
     uint16 *local_offsets, local_offset;
     uint32 type_idx, func_idx, local_idx, global_idx, table_idx;
@@ -10838,6 +10896,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
     mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32;
 #else
     mem_offset_type = VALUE_TYPE_I32;
+    table_elem_idx_type = VALUE_TYPE_I32;
 #endif
     uint32 memidx;
 
@@ -11997,8 +12056,13 @@ re_scan:
                 emit_uint32(loader_ctx, table_idx);
 #endif
 
+#if WASM_ENABLE_MEMORY64 != 0
+                table_elem_idx_type = is_table_64bit(module, table_idx)
+                                          ? VALUE_TYPE_I64
+                                          : VALUE_TYPE_I32;
+#endif
                 /* skip elem idx */
-                POP_I32();
+                POP_TBL_ELEM_IDX();
 
                 if (type_idx >= module->type_count) {
                     set_error_buf(error_buf, error_buf_size, "unknown type");
@@ -12375,8 +12439,8 @@ re_scan:
                 break;
             }
 
-            /* table.get x. tables[x]. [i32] -> [t] */
-            /* table.set x. tables[x]. [i32 t] -> [] */
+            /* table.get x. tables[x]. [it] -> [t] */
+            /* table.set x. tables[x]. [it t] -> [] */
             case WASM_OP_TABLE_GET:
             case WASM_OP_TABLE_SET:
             {
@@ -12407,8 +12471,13 @@ re_scan:
                 emit_uint32(loader_ctx, table_idx);
 #endif
 
+#if WASM_ENABLE_MEMORY64 != 0
+                table_elem_idx_type = is_table_64bit(module, table_idx)
+                                          ? VALUE_TYPE_I64
+                                          : VALUE_TYPE_I32;
+#endif
                 if (opcode == WASM_OP_TABLE_GET) {
-                    POP_I32();
+                    POP_TBL_ELEM_IDX();
 #if WASM_ENABLE_FAST_INTERP != 0
                     PUSH_OFFSET_TYPE(decl_ref_type);
 #endif
@@ -12419,7 +12488,7 @@ re_scan:
                     POP_OFFSET_TYPE(decl_ref_type);
 #endif
                     POP_TYPE(decl_ref_type);
-                    POP_I32();
+                    POP_TBL_ELEM_IDX();
                 }
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
@@ -14702,7 +14771,12 @@ re_scan:
 #endif
                         POP_I32();
                         POP_I32();
-                        POP_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        table_elem_idx_type = is_table_64bit(module, table_idx)
+                                                  ? VALUE_TYPE_I64
+                                                  : VALUE_TYPE_I32;
+#endif
+                        POP_TBL_ELEM_IDX();
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
                         module->is_ref_types_used = true;
@@ -14727,7 +14801,8 @@ re_scan:
                     }
                     case WASM_OP_TABLE_COPY:
                     {
-                        uint8 src_type, dst_type;
+                        uint8 src_type, dst_type, src_tbl_idx_type,
+                            dst_tbl_idx_type, min_tbl_idx_type;
 #if WASM_ENABLE_GC != 0
                         WASMRefType *src_ref_type = NULL, *dst_ref_type = NULL;
 #endif
@@ -14773,9 +14848,31 @@ re_scan:
                         emit_uint32(loader_ctx, dst_tbl_idx);
                         emit_uint32(loader_ctx, src_tbl_idx);
 #endif
-                        POP_I32();
-                        POP_I32();
-                        POP_I32();
+
+#if WASM_ENABLE_MEMORY64 != 0
+                        src_tbl_idx_type = is_table_64bit(module, src_tbl_idx)
+                                               ? VALUE_TYPE_I64
+                                               : VALUE_TYPE_I32;
+                        dst_tbl_idx_type = is_table_64bit(module, dst_tbl_idx)
+                                               ? VALUE_TYPE_I64
+                                               : VALUE_TYPE_I32;
+                        min_tbl_idx_type =
+                            (src_tbl_idx_type == VALUE_TYPE_I32
+                             || dst_tbl_idx_type == VALUE_TYPE_I32)
+                                ? VALUE_TYPE_I32
+                                : VALUE_TYPE_I64;
+#else
+                        src_tbl_idx_type = VALUE_TYPE_I32;
+                        dst_tbl_idx_type = VALUE_TYPE_I32;
+                        min_tbl_idx_type = VALUE_TYPE_I32;
+#endif
+
+                        table_elem_idx_type = min_tbl_idx_type;
+                        POP_TBL_ELEM_IDX();
+                        table_elem_idx_type = src_tbl_idx_type;
+                        POP_TBL_ELEM_IDX();
+                        table_elem_idx_type = dst_tbl_idx_type;
+                        POP_TBL_ELEM_IDX();
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
                         module->is_ref_types_used = true;
@@ -14795,7 +14892,12 @@ re_scan:
                         emit_uint32(loader_ctx, table_idx);
 #endif
 
-                        PUSH_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        table_elem_idx_type = is_table_64bit(module, table_idx)
+                                                  ? VALUE_TYPE_I64
+                                                  : VALUE_TYPE_I32;
+#endif
+                        PUSH_TBL_ELEM_IDX();
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
                         module->is_ref_types_used = true;
@@ -14844,15 +14946,20 @@ re_scan:
                         emit_uint32(loader_ctx, table_idx);
 #endif
 
-                        POP_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        table_elem_idx_type = is_table_64bit(module, table_idx)
+                                                  ? VALUE_TYPE_I64
+                                                  : VALUE_TYPE_I32;
+#endif
+                        POP_TBL_ELEM_IDX();
 #if WASM_ENABLE_FAST_INTERP != 0
                         POP_OFFSET_TYPE(decl_type);
 #endif
                         POP_TYPE(decl_type);
                         if (opcode1 == WASM_OP_TABLE_GROW)
-                            PUSH_I32();
+                            PUSH_TBL_ELEM_IDX();
                         else
-                            POP_I32();
+                            POP_TBL_ELEM_IDX();
 
 #if WASM_ENABLE_WAMR_COMPILER != 0
                         module->is_ref_types_used = true;

+ 176 - 34
core/iwasm/interpreter/wasm_mini_loader.c

@@ -33,12 +33,25 @@ has_module_memory64(WASMModule *module)
     /* TODO: multi-memories for now assuming the memory idx type is consistent
      * across multi-memories */
     if (module->import_memory_count > 0)
-        return !!(module->import_memories[0].u.mem_type.flags & MEMORY64_FLAG);
+        return !!(module->import_memories[0].u.memory.mem_type.flags
+                  & MEMORY64_FLAG);
     else if (module->memory_count > 0)
         return !!(module->memories[0].flags & MEMORY64_FLAG);
 
     return false;
 }
+
+static bool
+is_table_64bit(WASMModule *module, uint32 table_idx)
+{
+    if (table_idx < module->import_table_count)
+        return !!(module->import_tables[table_idx].u.table.table_type.flags
+                  & TABLE64_FLAG);
+    else
+        return !!(module->tables[table_idx].table_type.flags & TABLE64_FLAG);
+
+    return false;
+}
 #endif
 
 static void
@@ -577,11 +590,15 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
 }
 
 static void
-adjust_table_max_size(uint32 init_size, uint32 max_size_flag, uint32 *max_size)
+adjust_table_max_size(bool is_table64, uint32 init_size, uint32 max_size_flag,
+                      uint32 *max_size)
 {
     uint32 default_max_size = init_size * 2 > WASM_TABLE_MAX_SIZE
                                   ? init_size * 2
                                   : WASM_TABLE_MAX_SIZE;
+    /* TODO: current still use UINT32_MAX as upper limit for table size to keep
+     * ABI unchanged */
+    (void)is_table64;
 
     if (max_size_flag) {
         /* module defines the table limitation */
@@ -642,8 +659,8 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
                   char *error_buf, uint32 error_buf_size)
 {
     const uint8 *p = *p_buf, *p_end = buf_end;
-    uint32 declare_elem_type = 0, declare_max_size_flag = 0,
-           declare_init_size = 0, declare_max_size = 0;
+    uint32 declare_elem_type = 0, table_flag = 0, declare_init_size = 0,
+           declare_max_size = 0;
 
     CHECK_BUF(p, p_end, 1);
     /* 0x70 or 0x6F */
@@ -654,24 +671,29 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
 #endif
     );
 
-    read_leb_uint32(p, p_end, declare_max_size_flag);
+    read_leb_uint32(p, p_end, table_flag);
+
+    if (!wasm_table_check_flags(table_flag, error_buf, error_buf_size, false)) {
+        return false;
+    }
+
     read_leb_uint32(p, p_end, declare_init_size);
-    if (declare_max_size_flag & 1) {
+    if (table_flag & MAX_TABLE_SIZE_FLAG) {
         read_leb_uint32(p, p_end, declare_max_size);
         bh_assert(table->table_type.init_size <= table->table_type.max_size);
     }
 
-    adjust_table_max_size(declare_init_size, declare_max_size_flag,
-                          &declare_max_size);
+    adjust_table_max_size(table_flag & TABLE64_FLAG, declare_init_size,
+                          table_flag & MAX_TABLE_SIZE_FLAG, &declare_max_size);
     *p_buf = p;
 
-    bh_assert(
-        !((declare_max_size_flag & 1) && declare_init_size > declare_max_size));
+    bh_assert(!((table_flag & MAX_TABLE_SIZE_FLAG)
+                && declare_init_size > declare_max_size));
 
     /* now we believe all declaration are ok */
     table->table_type.elem_type = declare_elem_type;
     table->table_type.init_size = declare_init_size;
-    table->table_type.flags = declare_max_size_flag;
+    table->table_type.flags = table_flag;
     table->table_type.max_size = declare_max_size;
     return true;
 }
@@ -789,16 +811,22 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table,
     p_org = p;
     read_leb_uint32(p, p_end, table->table_type.flags);
     bh_assert(p - p_org <= 1);
-    bh_assert(table->table_type.flags <= 1);
     (void)p_org;
 
+    if (!wasm_table_check_flags(table->table_type.flags, error_buf,
+                                error_buf_size, false)) {
+        return false;
+    }
+
     read_leb_uint32(p, p_end, table->table_type.init_size);
-    if (table->table_type.flags == 1) {
+    if (table->table_type.flags == MAX_TABLE_SIZE_FLAG) {
         read_leb_uint32(p, p_end, table->table_type.max_size);
         bh_assert(table->table_type.init_size <= table->table_type.max_size);
     }
 
-    adjust_table_max_size(table->table_type.init_size, table->table_type.flags,
+    adjust_table_max_size(table->table_type.flags & TABLE64_FLAG,
+                          table->table_type.init_size,
+                          table->table_type.flags & MAX_TABLE_SIZE_FLAG,
                           &table->table_type.max_size);
 
     *p_buf = p;
@@ -1575,6 +1603,7 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                            uint32 error_buf_size)
 {
     const uint8 *p = buf, *p_end = buf_end;
+    uint8 table_elem_idx_type;
     uint32 table_segment_count, i, table_index, function_count;
     uint64 total_size;
     WASMTableSeg *table_segment;
@@ -1592,6 +1621,7 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
         table_segment = module->table_segments;
         for (i = 0; i < table_segment_count; i++, table_segment++) {
             bh_assert(p < p_end);
+            table_elem_idx_type = VALUE_TYPE_I32;
 
 #if WASM_ENABLE_REF_TYPES != 0
             read_leb_uint32(p, p_end, table_segment->mode);
@@ -1608,9 +1638,15 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                                            error_buf, error_buf_size))
                         return false;
 
+#if WASM_ENABLE_MEMORY64 != 0
+                    table_elem_idx_type =
+                        is_table_64bit(module, table_segment->table_index)
+                            ? VALUE_TYPE_I64
+                            : VALUE_TYPE_I32;
+#endif
                     if (!load_init_expr(
                             module, &p, p_end, &table_segment->base_offset,
-                            VALUE_TYPE_I32, error_buf, error_buf_size))
+                            table_elem_idx_type, error_buf, error_buf_size))
                         return false;
 
                     if (table_segment->mode == 0) {
@@ -1646,9 +1682,15 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                                           &table_segment->table_index,
                                           error_buf, error_buf_size))
                         return false;
+#if WASM_ENABLE_MEMORY64 != 0
+                    table_elem_idx_type =
+                        is_table_64bit(module, table_segment->table_index)
+                            ? VALUE_TYPE_I64
+                            : VALUE_TYPE_I32;
+#endif
                     if (!load_init_expr(
                             module, &p, p_end, &table_segment->base_offset,
-                            VALUE_TYPE_I32, error_buf, error_buf_size))
+                            table_elem_idx_type, error_buf, error_buf_size))
                         return false;
                     if (!load_elem_type(&p, p_end, &table_segment->elem_type,
                                         table_segment->mode == 2 ? true : false,
@@ -1691,13 +1733,29 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
                                   &table_segment->table_index, error_buf,
                                   error_buf_size))
                 return false;
+#if WASM_ENABLE_MEMORY64 != 0
+            table_elem_idx_type =
+                is_table_64bit(module, table_segment->table_index)
+                    ? VALUE_TYPE_I64
+                    : VALUE_TYPE_I32;
+#endif
             if (!load_init_expr(module, &p, p_end, &table_segment->base_offset,
-                                VALUE_TYPE_I32, error_buf, error_buf_size))
+                                table_elem_idx_type, error_buf, error_buf_size))
                 return false;
             if (!load_func_index_vec(&p, p_end, module, table_segment,
                                      error_buf, error_buf_size))
                 return false;
 #endif /* WASM_ENABLE_REF_TYPES != 0 */
+
+#if WASM_ENABLE_MEMORY64 != 0
+            if (table_elem_idx_type == VALUE_TYPE_I64
+                && table_segment->base_offset.u.u64 > UINT32_MAX) {
+                set_error_buf(error_buf, error_buf_size,
+                              "In table64, table base offset can't be "
+                              "larger than UINT32_MAX");
+                return false;
+            }
+#endif
         }
     }
 
@@ -1781,8 +1839,8 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end,
                 /* This memory_flag is from memory instead of data segment */
                 uint8 memory_flag;
                 if (module->import_memory_count > 0) {
-                    memory_flag =
-                        module->import_memories[mem_index].u.mem_type.flags;
+                    memory_flag = module->import_memories[mem_index]
+                                      .u.memory.mem_type.flags;
                 }
                 else {
                     memory_flag =
@@ -2948,6 +3006,12 @@ load_from_sections(WASMModule *module, WASMSection *sections,
         }
     }
 
+#if WASM_ENABLE_MEMORY64 != 0
+    if (!check_memory64_flags_consistency(module, error_buf, error_buf_size,
+                                          false))
+        return false;
+#endif
+
     calculate_global_data_offset(module);
 
 #if WASM_ENABLE_FAST_JIT != 0
@@ -5214,6 +5278,13 @@ fail:
     } while (0)
 #define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET()
 
+#define PUSH_TBL_ELEM_IDX()                                               \
+    do {                                                                  \
+        if (!(wasm_loader_push_frame_ref(loader_ctx, table_elem_idx_type, \
+                                         error_buf, error_buf_size)))     \
+            goto fail;                                                    \
+    } while (0)
+
 #define POP_MEM_OFFSET()                                                   \
     do {                                                                   \
         if (!wasm_loader_pop_frame_ref_offset(loader_ctx, mem_offset_type, \
@@ -5221,6 +5292,13 @@ fail:
             goto fail;                                                     \
     } while (0)
 
+#define POP_TBL_ELEM_IDX()                                               \
+    do {                                                                 \
+        if (!(wasm_loader_pop_frame_ref(loader_ctx, table_elem_idx_type, \
+                                        error_buf, error_buf_size)))     \
+            goto fail;                                                   \
+    } while (0)
+
 #define POP_AND_PUSH(type_pop, type_push)                         \
     do {                                                          \
         if (!(wasm_loader_push_pop_frame_ref_offset(              \
@@ -5284,6 +5362,13 @@ fail:
 
 #define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET()
 
+#define PUSH_TBL_ELEM_IDX()                                               \
+    do {                                                                  \
+        if (!(wasm_loader_push_frame_ref(loader_ctx, table_elem_idx_type, \
+                                         error_buf, error_buf_size)))     \
+            goto fail;                                                    \
+    } while (0)
+
 #define POP_I32()                                                              \
     do {                                                                       \
         if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I32, error_buf, \
@@ -5326,6 +5411,13 @@ fail:
             goto fail;                                               \
     } while (0)
 
+#define POP_TBL_ELEM_IDX()                                               \
+    do {                                                                 \
+        if (!(wasm_loader_pop_frame_ref(loader_ctx, table_elem_idx_type, \
+                                        error_buf, error_buf_size)))     \
+            goto fail;                                                   \
+    } while (0)
+
 #define POP_AND_PUSH(type_pop, type_push)                              \
     do {                                                               \
         if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 1, type_push, \
@@ -5945,7 +6037,8 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
 {
     uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org;
     uint32 param_count, local_count, global_count;
-    uint8 *param_types, *local_types, local_type, global_type, mem_offset_type;
+    uint8 *param_types, *local_types, local_type, global_type, mem_offset_type,
+        table_elem_idx_type;
     BlockType func_block_type;
     uint16 *local_offsets, local_offset;
     uint32 count, local_idx, global_idx, u32, align, i, memidx;
@@ -5976,6 +6069,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
     mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32;
 #else
     mem_offset_type = VALUE_TYPE_I32;
+    table_elem_idx_type = VALUE_TYPE_I32;
 #endif
 
     global_count = module->import_global_count + module->global_count;
@@ -6588,8 +6682,13 @@ re_scan:
                 emit_uint32(loader_ctx, table_idx);
 #endif
 
+#if WASM_ENABLE_MEMORY64 != 0
+                table_elem_idx_type = is_table_64bit(module, table_idx)
+                                          ? VALUE_TYPE_I64
+                                          : VALUE_TYPE_I32;
+#endif
                 /* skip elem idx */
-                POP_I32();
+                POP_TBL_ELEM_IDX();
 
                 bh_assert(type_idx < module->type_count);
 
@@ -6865,8 +6964,8 @@ re_scan:
                 break;
             }
 
-            /* table.get x. tables[x]. [i32] -> [t] */
-            /* table.set x. tables[x]. [i32 t] -> [] */
+            /* table.get x. tables[x]. [it] -> [t] */
+            /* table.set x. tables[x]. [it t] -> [] */
             case WASM_OP_TABLE_GET:
             case WASM_OP_TABLE_SET:
             {
@@ -6882,8 +6981,13 @@ re_scan:
                 emit_uint32(loader_ctx, table_idx);
 #endif
 
+#if WASM_ENABLE_MEMORY64 != 0
+                table_elem_idx_type = is_table_64bit(module, table_idx)
+                                          ? VALUE_TYPE_I64
+                                          : VALUE_TYPE_I32;
+#endif
                 if (opcode == WASM_OP_TABLE_GET) {
-                    POP_I32();
+                    POP_TBL_ELEM_IDX();
 #if WASM_ENABLE_FAST_INTERP != 0
                     PUSH_OFFSET_TYPE(decl_ref_type);
 #endif
@@ -6894,7 +6998,7 @@ re_scan:
                     POP_OFFSET_TYPE(decl_ref_type);
 #endif
                     POP_TYPE(decl_ref_type);
-                    POP_I32();
+                    POP_TBL_ELEM_IDX();
                 }
                 break;
             }
@@ -7819,7 +7923,12 @@ re_scan:
 #endif
                         POP_I32();
                         POP_I32();
-                        POP_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        table_elem_idx_type = is_table_64bit(module, table_idx)
+                                                  ? VALUE_TYPE_I64
+                                                  : VALUE_TYPE_I32;
+#endif
+                        POP_TBL_ELEM_IDX();
                         break;
                     }
                     case WASM_OP_ELEM_DROP:
@@ -7838,7 +7947,8 @@ re_scan:
                     case WASM_OP_TABLE_COPY:
                     {
                         uint8 src_ref_type, dst_ref_type;
-                        uint32 src_tbl_idx, dst_tbl_idx;
+                        uint32 src_tbl_idx, dst_tbl_idx, src_tbl_idx_type,
+                            dst_tbl_idx_type, min_tbl_idx_type;
 
                         read_leb_uint32(p, p_end, src_tbl_idx);
                         if (!get_table_elem_type(module, src_tbl_idx,
@@ -7862,9 +7972,31 @@ re_scan:
                         emit_uint32(loader_ctx, src_tbl_idx);
                         emit_uint32(loader_ctx, dst_tbl_idx);
 #endif
-                        POP_I32();
-                        POP_I32();
-                        POP_I32();
+
+#if WASM_ENABLE_MEMORY64 != 0
+                        src_tbl_idx_type = is_table_64bit(module, src_tbl_idx)
+                                               ? VALUE_TYPE_I64
+                                               : VALUE_TYPE_I32;
+                        dst_tbl_idx_type = is_table_64bit(module, dst_tbl_idx)
+                                               ? VALUE_TYPE_I64
+                                               : VALUE_TYPE_I32;
+                        min_tbl_idx_type =
+                            (src_tbl_idx_type == VALUE_TYPE_I32
+                             || dst_tbl_idx_type == VALUE_TYPE_I32)
+                                ? VALUE_TYPE_I32
+                                : VALUE_TYPE_I64;
+#else
+                        src_tbl_idx_type = VALUE_TYPE_I32;
+                        dst_tbl_idx_type = VALUE_TYPE_I32;
+                        min_tbl_idx_type = VALUE_TYPE_I32;
+#endif
+
+                        table_elem_idx_type = min_tbl_idx_type;
+                        POP_TBL_ELEM_IDX();
+                        table_elem_idx_type = src_tbl_idx_type;
+                        POP_TBL_ELEM_IDX();
+                        table_elem_idx_type = dst_tbl_idx_type;
+                        POP_TBL_ELEM_IDX();
                         break;
                     }
                     case WASM_OP_TABLE_SIZE:
@@ -7882,7 +8014,12 @@ re_scan:
                         emit_uint32(loader_ctx, table_idx);
 #endif
 
-                        PUSH_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        table_elem_idx_type = is_table_64bit(module, table_idx)
+                                                  ? VALUE_TYPE_I64
+                                                  : VALUE_TYPE_I32;
+#endif
+                        PUSH_TBL_ELEM_IDX();
                         break;
                     }
                     case WASM_OP_TABLE_GROW:
@@ -7914,15 +8051,20 @@ re_scan:
                         emit_uint32(loader_ctx, table_idx);
 #endif
 
-                        POP_I32();
+#if WASM_ENABLE_MEMORY64 != 0
+                        table_elem_idx_type = is_table_64bit(module, table_idx)
+                                                  ? VALUE_TYPE_I64
+                                                  : VALUE_TYPE_I32;
+#endif
+                        POP_TBL_ELEM_IDX();
 #if WASM_ENABLE_FAST_INTERP != 0
                         POP_OFFSET_TYPE(decl_ref_type);
 #endif
                         POP_TYPE(decl_ref_type);
                         if (opcode1 == WASM_OP_TABLE_GROW)
-                            PUSH_I32();
+                            PUSH_TBL_ELEM_IDX();
                         else
-                            POP_I32();
+                            PUSH_TBL_ELEM_IDX();
                         break;
                     }
 #endif /* WASM_ENABLE_REF_TYPES */

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

@@ -678,6 +678,8 @@ tables_instantiate(const WASMModule *module, WASMModuleInstance *module_inst,
            uninitialized elements */
 #endif
 
+        table->is_table64 = import->u.table.table_type.flags & TABLE64_FLAG;
+
 #if WASM_ENABLE_MULTI_MODULE != 0
         *table_linked = table_inst_linked;
         if (table_inst_linked != NULL) {
@@ -736,6 +738,7 @@ tables_instantiate(const WASMModule *module, WASMModuleInstance *module_inst,
         /* For GC, all elements have already been set to NULL_REF (0) as
            uninitialized elements */
 #endif
+        table->is_table64 = module->tables[i].table_type.flags & TABLE64_FLAG;
         table->elem_type = module->tables[i].table_type.elem_type;
 #if WASM_ENABLE_GC != 0
         table->elem_ref_type.elem_ref_type =

+ 2 - 1
core/iwasm/interpreter/wasm_runtime.h

@@ -157,7 +157,8 @@ struct WASMMemoryInstance {
 struct WASMTableInstance {
     /* The element type */
     uint8 elem_type;
-    uint8 __padding__[7];
+    uint8 is_table64;
+    uint8 __padding__[6];
     union {
 #if WASM_ENABLE_GC != 0
         WASMRefType *elem_ref_type;

+ 173 - 24
tests/wamr-test-suites/spec-test-script/memory64_ignore_cases.patch

@@ -1,5 +1,5 @@
 diff --git a/test/core/address.wast b/test/core/address.wast
-index 8e52030..de0d0cb 100644
+index 8e52030e..de0d0cb9 100644
 --- a/test/core/address.wast
 +++ b/test/core/address.wast
 @@ -210,7 +210,7 @@
@@ -12,7 +12,7 @@ index 8e52030..de0d0cb 100644
      "(memory 1)"
      "(func (drop (i32.load offset=4294967296 (i32.const 0))))"
 diff --git a/test/core/binary.wast b/test/core/binary.wast
-index 4090b2c..18f66b4 100644
+index 4090b2cd..18f66b42 100644
 --- a/test/core/binary.wast
 +++ b/test/core/binary.wast
 @@ -206,7 +206,7 @@
@@ -70,7 +70,7 @@ index 4090b2c..18f66b4 100644
  ;; Start section
  (module binary
 diff --git a/test/core/data.wast b/test/core/data.wast
-index 4f339be..0b5b3e6 100644
+index 4f339bed..0b5b3e6b 100644
 --- a/test/core/data.wast
 +++ b/test/core/data.wast
 @@ -306,9 +306,10 @@
@@ -124,7 +124,7 @@ index 4f339be..0b5b3e6 100644
  
  ;; Invalid offsets
 diff --git a/test/core/elem.wast b/test/core/elem.wast
-index 575ecef..dd1106c 100644
+index 575ecef8..dd1106c7 100644
 --- a/test/core/elem.wast
 +++ b/test/core/elem.wast
 @@ -562,6 +562,7 @@
@@ -151,7 +151,7 @@ index 575ecef..dd1106c 100644
  (assert_return (invoke $module1 "call-9") (i32.const 70))
 +;)
 diff --git a/test/core/global.wast b/test/core/global.wast
-index e40a305..8f8f25b 100644
+index e40a305f..8f8f25bb 100644
 --- a/test/core/global.wast
 +++ b/test/core/global.wast
 @@ -328,10 +328,12 @@
@@ -168,7 +168,7 @@ index e40a305..8f8f25b 100644
  (assert_invalid
    (module (global (import "test" "global-i32") i32) (global i32 (global.get 0) (global.get 0)))
 diff --git a/test/core/if.wast b/test/core/if.wast
-index 2ea45f6..b6dd504 100644
+index 2ea45f6f..b6dd5044 100644
 --- a/test/core/if.wast
 +++ b/test/core/if.wast
 @@ -527,11 +527,12 @@
@@ -199,7 +199,7 @@ index 2ea45f6..b6dd504 100644
  (assert_malformed
    (module quote
 diff --git a/test/core/imports.wast b/test/core/imports.wast
-index 69f76a0..a3844c6 100644
+index 69f76a0b..a3844c65 100644
 --- a/test/core/imports.wast
 +++ b/test/core/imports.wast
 @@ -572,6 +572,7 @@
@@ -219,7 +219,7 @@ index 69f76a0..a3844c6 100644
  
  ;; Syntax errors
 diff --git a/test/core/linking.wast b/test/core/linking.wast
-index 994e0f4..d0bfb5f 100644
+index 994e0f49..d0bfb5f6 100644
 --- a/test/core/linking.wast
 +++ b/test/core/linking.wast
 @@ -64,6 +64,7 @@
@@ -376,7 +376,7 @@ index 994e0f4..d0bfb5f 100644
  (assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
 +;)
 diff --git a/test/core/memory.wast b/test/core/memory.wast
-index 1dd5b84..497b69f 100644
+index 1dd5b845..497b69fc 100644
 --- a/test/core/memory.wast
 +++ b/test/core/memory.wast
 @@ -76,17 +76,17 @@
@@ -404,7 +404,7 @@ index 1dd5b84..497b69f 100644
  
  (module
 diff --git a/test/core/ref_func.wast b/test/core/ref_func.wast
-index adb5cb7..590f626 100644
+index adb5cb78..590f6262 100644
 --- a/test/core/ref_func.wast
 +++ b/test/core/ref_func.wast
 @@ -4,7 +4,8 @@
@@ -417,8 +417,65 @@ index adb5cb7..590f626 100644
    (func $g (param $x i32) (result i32)
      (i32.add (local.get $x) (i32.const 1))
    )
+diff --git a/test/core/table.wast b/test/core/table.wast
+index 1b6afe9b..45dd1145 100644
+--- a/test/core/table.wast
++++ b/test/core/table.wast
+@@ -8,16 +8,20 @@
+ (module (table 0 65536 funcref))
+ (module (table 0 0xffff_ffff funcref))
+ 
++(; TODO: wabt not unsupported gc yet
+ (module (table 1 (ref null func)))
+ (module (table 1 (ref null extern)))
+ (module (table 1 (ref null $t)) (type $t (func)))
++;)
+ 
+ (module (table 0 funcref) (table 0 funcref))
+ (module (table (import "spectest" "table") 0 funcref) (table 0 funcref))
+ 
++(; TODO: wabt not unsupported gc yet
+ (module (table 0 funcref (ref.null func)))
+ (module (table 1 funcref (ref.null func)))
+ (module (table 1 (ref null func) (ref.null func)))
++;)
+ 
+ (assert_invalid (module (elem (i32.const 0))) "unknown table")
+ (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table")
+@@ -31,6 +35,7 @@
+   "size minimum must not be greater than maximum"
+ )
+ 
++(; TODO: wabt not unsupported gc yet
+ (assert_invalid
+   (module quote "(table 0x1_0000_0000 funcref)")
+   "table size must be at most 2^32-1"
+@@ -43,6 +48,7 @@
+   (module quote "(table 0 0x1_0000_0000 funcref)")
+   "table size must be at most 2^32-1"
+ )
++;)
+ 
+ ;; Same as above but with i64 index types
+ 
+@@ -71,6 +77,7 @@
+ (assert_invalid (module (elem (i32.const 0))) "unknown table")
+ (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table")
+ 
++(; TODO: wabt not unsupported gc yet
+ (assert_invalid
+   (module (table 1 (ref null func) (i32.const 0)))
+   "type mismatch"
+@@ -159,6 +166,7 @@
+   )
+   "type mismatch"
+ )
++;)
+ 
+ 
+ ;; Duplicate table identifiers
 diff --git a/test/core/table_copy.wast b/test/core/table_copy.wast
-index 380e84e..f37e745 100644
+index 613fc529..abeca22c 100644
 --- a/test/core/table_copy.wast
 +++ b/test/core/table_copy.wast
 @@ -14,11 +14,12 @@
@@ -728,8 +785,46 @@ index 380e84e..f37e745 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/table_grow.wast b/test/core/table_grow.wast
+index e0872d78..6a84f239 100644
+--- a/test/core/table_grow.wast
++++ b/test/core/table_grow.wast
+@@ -147,19 +147,20 @@
+ )
+ (register "grown-table" $Tgt)
+ (assert_return (invoke $Tgt "grow") (i32.const 1)) ;; now size is 2
+-(module $Tgit1
+-  ;; imported table limits should match, because external table size is 2 now
+-  (table (export "table") (import "grown-table" "table") 2 funcref)
+-  (func (export "grow") (result i32) (table.grow (ref.null func) (i32.const 1)))
+-)
+-(register "grown-imported-table" $Tgit1)
+-(assert_return (invoke $Tgit1 "grow") (i32.const 2)) ;; now size is 3
+-(module $Tgit2
+-  ;; imported table limits should match, because external table size is 3 now
+-  (import "grown-imported-table" "table" (table 3 funcref))
+-  (func (export "size") (result i32) (table.size))
+-)
+-(assert_return (invoke $Tgit2 "size") (i32.const 3))
++;; TODO: No dynnamic linking yet
++;; (module $Tgit1
++;;   ;; imported table limits should match, because external table size is 2 now
++;;   (table (export "table") (import "grown-table" "table") 2 funcref)
++;;   (func (export "grow") (result i32) (table.grow (ref.null func) (i32.const 1)))
++;; )
++;; (register "grown-imported-table" $Tgit1)
++;; (assert_return (invoke $Tgit1 "grow") (i32.const 2)) ;; now size is 3
++;; (module $Tgit2
++;;   ;; imported table limits should match, because external table size is 3 now
++;;   (import "grown-imported-table" "table" (table 3 funcref))
++;;   (func (export "size") (result i32) (table.size))
++;; )
++;; (assert_return (invoke $Tgit2 "size") (i32.const 3))
+ 
+ 
+ ;; Type errors
 diff --git a/test/core/table_init.wast b/test/core/table_init.wast
-index 0b2d26f..bdab6a0 100644
+index 5c3679ab..76782794 100644
 --- a/test/core/table_init.wast
 +++ b/test/core/table_init.wast
 @@ -14,11 +14,12 @@
@@ -749,8 +844,8 @@ index 0b2d26f..bdab6a0 100644
 +  (func (result i32) (i32.const 4))    ;; index 4
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
-   (elem (table $t0) (i32.const 2) func 3 1 4 1)
-@@ -72,11 +73,12 @@
+   (table $t2 i64 30 30 funcref)
+@@ -73,11 +74,12 @@
  
  (module
    (type (func (result i32)))  ;; type #0
@@ -767,8 +862,8 @@ index 0b2d26f..bdab6a0 100644
 +  (func (result i32) (i32.const 4))    ;; index 4
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
-   (elem (table $t0) (i32.const 2) func 3 1 4 1)
-@@ -130,11 +132,12 @@
+   (table $t2 i64 30 30 funcref)
+@@ -132,11 +134,12 @@
  
  (module
    (type (func (result i32)))  ;; type #0
@@ -785,8 +880,8 @@ index 0b2d26f..bdab6a0 100644
 +  (func (result i32) (i32.const 4))    ;; index 4
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
-   (elem (table $t0) (i32.const 2) func 3 1 4 1)
-@@ -196,11 +199,12 @@
+   (table $t2 i64 30 30 funcref)
+@@ -199,11 +202,12 @@
  
  (module
    (type (func (result i32)))  ;; type #0
@@ -803,8 +898,8 @@ index 0b2d26f..bdab6a0 100644
 +  (func (result i32) (i32.const 4))    ;; index 4
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
-   (elem (table $t1) (i32.const 2) func 3 1 4 1)
-@@ -254,11 +258,12 @@
+   (table $t2 i64 30 30 funcref)
+@@ -258,11 +262,12 @@
  
  (module
    (type (func (result i32)))  ;; type #0
@@ -821,8 +916,8 @@ index 0b2d26f..bdab6a0 100644
 +  (func (result i32) (i32.const 4))    ;; index 4
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
-   (elem (table $t1) (i32.const 2) func 3 1 4 1)
-@@ -312,11 +317,12 @@
+   (table $t2 i64 30 30 funcref)
+@@ -317,11 +322,12 @@
  
  (module
    (type (func (result i32)))  ;; type #0
@@ -839,9 +934,63 @@ index 0b2d26f..bdab6a0 100644
 +  (func (result i32) (i32.const 4))    ;; index 4
    (table $t0 30 30 funcref)
    (table $t1 30 30 funcref)
-   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+   (table $t2 i64 30 30 funcref)
+@@ -384,11 +390,12 @@
+ 
+ (module
+   (type (func (result i32)))  ;; type #0
+-  (import "a" "ef0" (func (result i32)))    ;; index 0
+-  (import "a" "ef1" (func (result i32)))
+-  (import "a" "ef2" (func (result i32)))
+-  (import "a" "ef3" (func (result i32)))
+-  (import "a" "ef4" (func (result i32)))    ;; index 4
++  ;; aot mode does not support module linking
++  (func (result i32) (i32.const 0))    ;; index 0
++  (func (result i32) (i32.const 1))
++  (func (result i32) (i32.const 2))
++  (func (result i32) (i32.const 3))
++  (func (result i32) (i32.const 4))    ;; index 4
+   (table $t0 30 30 funcref)
+   (table $t1 30 30 funcref)
+   (table $t2 i64 30 30 funcref)
+@@ -443,11 +450,12 @@
+ 
+ (module
+   (type (func (result i32)))  ;; type #0
+-  (import "a" "ef0" (func (result i32)))    ;; index 0
+-  (import "a" "ef1" (func (result i32)))
+-  (import "a" "ef2" (func (result i32)))
+-  (import "a" "ef3" (func (result i32)))
+-  (import "a" "ef4" (func (result i32)))    ;; index 4
++  ;; aot mode does not support module linking
++  (func (result i32) (i32.const 0))    ;; index 0
++  (func (result i32) (i32.const 1))
++  (func (result i32) (i32.const 2))
++  (func (result i32) (i32.const 3))
++  (func (result i32) (i32.const 4))    ;; index 4
+   (table $t0 30 30 funcref)
+   (table $t1 30 30 funcref)
+   (table $t2 i64 30 30 funcref)
+@@ -502,11 +510,12 @@
+ 
+ (module
+   (type (func (result i32)))  ;; type #0
+-  (import "a" "ef0" (func (result i32)))    ;; index 0
+-  (import "a" "ef1" (func (result i32)))
+-  (import "a" "ef2" (func (result i32)))
+-  (import "a" "ef3" (func (result i32)))
+-  (import "a" "ef4" (func (result i32)))    ;; index 4
++  ;; aot mode does not support module linking
++  (func (result i32) (i32.const 0))    ;; index 0
++  (func (result i32) (i32.const 1))
++  (func (result i32) (i32.const 2))
++  (func (result i32) (i32.const 3))
++  (func (result i32) (i32.const 4))    ;; index 4
+   (table $t0 30 30 funcref)
+   (table $t1 30 30 funcref)
+   (table $t2 i64 30 30 funcref)
 diff --git a/test/core/unreached-valid.wast b/test/core/unreached-valid.wast
-index b7ebabf..4f2abfb 100644
+index b7ebabfd..4f2abfbf 100644
 --- a/test/core/unreached-valid.wast
 +++ b/test/core/unreached-valid.wast
 @@ -46,6 +46,7 @@

+ 4 - 2
tests/wamr-test-suites/test_wamr.sh

@@ -375,7 +375,7 @@ function sightglass_test()
 
 function setup_wabt()
 {
-    WABT_VERSION=1.0.34
+    WABT_VERSION=1.0.36
     if [ ${WABT_BINARY_RELEASE} == "YES" ]; then
         echo "download a binary release and install"
         local WAT2WASM=${WORK_DIR}/wabt/out/gcc/Release/wat2wasm
@@ -384,7 +384,7 @@ function setup_wabt()
                 cosmopolitan)
                     ;;
                 linux)
-                    WABT_PLATFORM=ubuntu
+                    WABT_PLATFORM=ubuntu-20.04
                     ;;
                 darwin)
                     WABT_PLATFORM=macos-12
@@ -502,6 +502,8 @@ function spec_test()
         # Reset to commit: "Merge remote-tracking branch 'upstream/main' into merge2"
         git reset --hard 48e69f394869c55b7bbe14ac963c09f4605490b6
         git checkout 044d0d2e77bdcbe891f7e0b9dd2ac01d56435f0b -- test/core/elem.wast test/core/data.wast
+        # Patch table64 extension 
+        git checkout 940398cd4823522a9b36bec4984be4b153dedb81 -- test/core/call_indirect.wast test/core/table.wast test/core/table_copy.wast test/core/table_copy_mixed.wast test/core/table_fill.wast test/core/table_get.wast test/core/table_grow.wast test/core/table_init.wast test/core/table_set.wast test/core/table_size.wast
         git apply ../../spec-test-script/memory64_ignore_cases.patch || exit 1
     elif [[ ${ENABLE_MULTI_MEMORY} == 1 ]]; then
         echo "checkout spec for multi memory proposal"