فهرست منبع

Support extended constant expressions (#4432)

* implement extended const expr (#4318)
* add a toggle to enable extended const on wamrc (#4412)
TianlongLiang 6 ماه پیش
والد
کامیت
7d05dbc988

+ 3 - 0
.github/workflows/compilation_on_android_ubuntu.yml

@@ -69,6 +69,7 @@ env:
   GC_TEST_OPTIONS: "-s spec -G -b -P"
   MEMORY64_TEST_OPTIONS: "-s spec -W -b -P"
   MULTI_MEMORY_TEST_OPTIONS: "-s spec -E -b -P"
+  EXTENDED_CONST_EXPR_TEST_OPTIONS: "-s spec -N -b -P"
 
 permissions:
   contents: read
@@ -164,6 +165,7 @@ jobs:
             "-DWAMR_BUILD_MEMORY64=1",
             "-DWAMR_BUILD_MULTI_MEMORY=1",
             "-DWAMR_BUILD_SHARED=1",
+            "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1",
           ]
         os: [ubuntu-22.04]
         platform: [android, linux]
@@ -609,6 +611,7 @@ jobs:
             $GC_TEST_OPTIONS,
             $MEMORY64_TEST_OPTIONS,
             $MULTI_MEMORY_TEST_OPTIONS,
+            $EXTENDED_CONST_EXPR_TEST_OPTIONS,
           ]
         include:
           - os: ubuntu-22.04

+ 1 - 0
.github/workflows/compilation_on_macos.yml

@@ -142,6 +142,7 @@ jobs:
             "-DWAMR_BUILD_SIMD=1",
             "-DWAMR_BUILD_TAIL_CALL=1",
             "-DWAMR_DISABLE_HW_BOUND_CHECK=1",
+            "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1",
           ]
         os: [macos-13]
         platform: [darwin]

+ 1 - 0
.github/workflows/compilation_on_sgx.yml

@@ -100,6 +100,7 @@ jobs:
             "-DWAMR_BUILD_MULTI_MODULE=1",
             "-DWAMR_BUILD_PERF_PROFILING=1",
             "-DWAMR_BUILD_REF_TYPES=1",
+            "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1",
             # doesn't support
             "-DWAMR_BUILD_SIMD=0",
             "-DWAMR_BUILD_TAIL_CALL=1",

+ 3 - 0
.github/workflows/nightly_run.yml

@@ -37,6 +37,7 @@ env:
   MULTI_TIER_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=1"
   # For Spec Test
   DEFAULT_TEST_OPTIONS: "-s spec -b -P"
+  EXTENDED_CONST_EXPR_TEST_OPTIONS: "-s spec -b -P -N"
   MULTI_MODULES_TEST_OPTIONS: "-s spec -b -P -M"
   SIMD_TEST_OPTIONS: "-s spec -b -P -S"
   THREADS_TEST_OPTIONS: "-s spec -b -P -p"
@@ -128,6 +129,7 @@ jobs:
             "-DWAMR_BUILD_MEMORY64=1",
             "-DWAMR_BUILD_MULTI_MEMORY=1",
             "-DWAMR_BUILD_SHARED=1",
+            "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1",
           ]
         os: [ubuntu-22.04]
         platform: [android, linux]
@@ -588,6 +590,7 @@ jobs:
             $DEFAULT_TEST_OPTIONS,
             $MULTI_MODULES_TEST_OPTIONS,
             $SIMD_TEST_OPTIONS,
+            $EXTENDED_CONST_EXPR_TEST_OPTIONS,
             $THREADS_TEST_OPTIONS,
             $WASI_TEST_OPTIONS,
           ]

+ 12 - 2
build-scripts/config_common.cmake

@@ -211,6 +211,10 @@ if (NOT DEFINED WAMR_BUILD_TAIL_CALL)
   set (WAMR_BUILD_TAIL_CALL 0)
 endif ()
 
+if (NOT DEFINED WAMR_BUILD_EXTENDED_CONST_EXPR)
+  set (WAMR_BUILD_EXTENDED_CONST_EXPR 0)
+endif ()
+
 ########################################
 # Compilation options to marco
 ########################################
@@ -673,7 +677,13 @@ if (WAMR_BUILD_INSTRUCTION_METERING EQUAL 1)
   message ("     Instruction metering enabled")
   add_definitions (-DWASM_ENABLE_INSTRUCTION_METERING=1)
 endif ()
-
+if (WAMR_BUILD_EXTENDED_CONST_EXPR EQUAL 1)
+  message ("     Extended constant expression enabled")
+  add_definitions(-DWASM_ENABLE_EXTENDED_CONST_EXPR=1)
+else()
+  message ("     Extended constant expression disabled")
+  add_definitions(-DWASM_ENABLE_EXTENDED_CONST_EXPR=0)
+endif ()
 ########################################
 # Show Phase4 Wasm proposals status.
 ########################################
@@ -687,6 +697,7 @@ message (
 "       \"WebAssembly C and C++ API\"\n"
 "     Configurable. 0 is OFF. 1 is ON:\n"
 "       \"Bulk Memory Operation\" via WAMR_BUILD_BULK_MEMORY: ${WAMR_BUILD_BULK_MEMORY}\n"
+"       \"Extended Constant Expressions\" via WAMR_BUILD_EXTENDED_CONST_EXPR: ${WAMR_BUILD_EXTENDED_CONST_EXPR}\n"
 "       \"Fixed-width SIMD\" via WAMR_BUILD_SIMD: ${WAMR_BUILD_SIMD}\n"
 "       \"Garbage collection\" via WAMR_BUILD_GC: ${WAMR_BUILD_GC}\n"
 "       \"Legacy Exception handling\" via WAMR_BUILD_EXCE_HANDLING: ${WAMR_BUILD_EXCE_HANDLING}\n"
@@ -701,7 +712,6 @@ message (
 "       \"Branch Hinting\"\n"
 "       \"Custom Annotation Syntax in the Text Format\"\n"
 "       \"Exception handling\"\n"
-"       \"Extended Constant Expressions\"\n"
 "       \"Import/Export of Mutable Globals\"\n"
 "       \"JS String Builtins\"\n"
 "       \"Relaxed SIMD\"\n"

+ 4 - 0
core/config.h

@@ -720,4 +720,8 @@ unless used elsewhere */
 #define WASM_ENABLE_INSTRUCTION_METERING 0
 #endif
 
+#ifndef WASM_ENABLE_EXTENDED_CONST_EXPR
+#define WASM_ENABLE_EXTENDED_CONST_EXPR 0
+#endif
+
 #endif /* end of _CONFIG_H_ */

+ 91 - 36
core/iwasm/aot/aot_loader.c

@@ -968,6 +968,35 @@ fail:
     return false;
 }
 
+#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+static void
+destroy_init_expr(InitializerExpression *expr)
+{
+#if WASM_ENABLE_GC != 0
+    if (expr->init_expr_type == INIT_EXPR_TYPE_STRUCT_NEW
+        || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW
+        || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) {
+        wasm_runtime_free(expr->u.unary.v.data);
+    }
+#endif
+
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    // free left expr and right expr for binary oprand
+    if (!is_expr_binary_op(expr->init_expr_type)) {
+        return;
+    }
+    if (expr->u.binary.l_expr) {
+        destroy_init_expr_recursive(expr->u.binary.l_expr);
+    }
+    if (expr->u.binary.r_expr) {
+        destroy_init_expr_recursive(expr->u.binary.r_expr);
+    }
+    expr->u.binary.l_expr = expr->u.binary.r_expr = NULL;
+#endif
+}
+#endif /* end of WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0 \
+        */
+
 static void
 destroy_import_memories(AOTImportMemory *import_memories)
 {
@@ -993,6 +1022,10 @@ destroy_mem_init_data_list(AOTModule *module, AOTMemInitData **data_list,
             /* If the module owns the binary data, free the bytes buffer */
             if (module->is_binary_freeable && data_list[i]->bytes)
                 wasm_runtime_free(data_list[i]->bytes);
+
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            destroy_init_expr(&data_list[i]->offset);
+#endif
             /* Free the data segment structure itself */
             wasm_runtime_free(data_list[i]);
         }
@@ -1043,11 +1076,11 @@ load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end,
         uint32 byte_count;
         uint32 is_passive;
         uint32 memory_index;
-        InitializerExpression init_value;
+        InitializerExpression offset_expr;
 
         read_uint32(buf, buf_end, is_passive);
         read_uint32(buf, buf_end, memory_index);
-        if (!load_init_expr(&buf, buf_end, module, &init_value, error_buf,
+        if (!load_init_expr(&buf, buf_end, module, &offset_expr, error_buf,
                             error_buf_size)) {
             return false;
         }
@@ -1062,8 +1095,7 @@ load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end,
         data_list[i]->is_passive = (bool)is_passive;
         data_list[i]->memory_index = memory_index;
 #endif
-        data_list[i]->offset.init_expr_type = init_value.init_expr_type;
-        data_list[i]->offset.u = init_value.u;
+        data_list[i]->offset = offset_expr;
         data_list[i]->byte_count = byte_count;
         data_list[i]->bytes = NULL;
         /* If the module owns the binary data, clone the bytes buffer */
@@ -1148,18 +1180,6 @@ fail:
     return false;
 }
 
-#if WASM_ENABLE_GC != 0
-static void
-destroy_init_expr(InitializerExpression *expr)
-{
-    if (expr->init_expr_type == INIT_EXPR_TYPE_STRUCT_NEW
-        || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW
-        || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) {
-        wasm_runtime_free(expr->u.data);
-    }
-}
-#endif /* end of WASM_ENABLE_GC != 0 */
-
 static void
 destroy_import_tables(AOTImportTable *import_tables)
 {
@@ -1183,6 +1203,9 @@ destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count)
             for (j = 0; j < data_list[i]->value_count; j++) {
                 destroy_init_expr(&data_list[i]->init_values[j]);
             }
+#endif
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            destroy_init_expr(&data_list[i]->offset);
 #endif
             wasm_runtime_free(data_list[i]);
         }
@@ -1208,34 +1231,34 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             break;
         case INIT_EXPR_TYPE_I32_CONST:
         case INIT_EXPR_TYPE_F32_CONST:
-            read_uint32(buf, buf_end, expr->u.i32);
+            read_uint32(buf, buf_end, expr->u.unary.v.i32);
             break;
         case INIT_EXPR_TYPE_I64_CONST:
         case INIT_EXPR_TYPE_F64_CONST:
-            read_uint64(buf, buf_end, expr->u.i64);
+            read_uint64(buf, buf_end, expr->u.unary.v.i64);
             break;
         case INIT_EXPR_TYPE_V128_CONST:
-            i64x2 = (uint64 *)expr->u.v128.i64x2;
+            i64x2 = (uint64 *)expr->u.unary.v.v128.i64x2;
             CHECK_BUF(buf, buf_end, sizeof(uint64) * 2);
             wasm_runtime_read_v128(buf, &i64x2[0], &i64x2[1]);
             buf += sizeof(uint64) * 2;
             break;
         case INIT_EXPR_TYPE_GET_GLOBAL:
-            read_uint32(buf, buf_end, expr->u.global_index);
+            read_uint32(buf, buf_end, expr->u.unary.v.global_index);
             break;
         /* INIT_EXPR_TYPE_FUNCREF_CONST can be used when
            both reference types and GC are disabled */
         case INIT_EXPR_TYPE_FUNCREF_CONST:
-            read_uint32(buf, buf_end, expr->u.ref_index);
+            read_uint32(buf, buf_end, expr->u.unary.v.ref_index);
             break;
 #if WASM_ENABLE_GC != 0 || WASM_ENABLE_REF_TYPES != 0
         case INIT_EXPR_TYPE_REFNULL_CONST:
-            read_uint32(buf, buf_end, expr->u.ref_index);
+            read_uint32(buf, buf_end, expr->u.unary.v.ref_index);
             break;
 #endif /* end of WASM_ENABLE_GC != 0 || WASM_ENABLE_REF_TYPES != 0 */
 #if WASM_ENABLE_GC != 0
         case INIT_EXPR_TYPE_I31_NEW:
-            read_uint32(buf, buf_end, expr->u.i32);
+            read_uint32(buf, buf_end, expr->u.unary.v.i32);
             break;
         case INIT_EXPR_TYPE_STRUCT_NEW:
         {
@@ -1256,7 +1279,7 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             free_if_fail = true;
             init_values->count = field_count;
             init_values->type_idx = type_idx;
-            expr->u.data = init_values;
+            expr->u.unary.v.data = init_values;
 
             if (type_idx >= module->type_count) {
                 set_error_buf(error_buf, error_buf_size,
@@ -1294,7 +1317,7 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             break;
         }
         case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT:
-            read_uint32(buf, buf_end, expr->u.type_index);
+            read_uint32(buf, buf_end, expr->u.unary.v.type_index);
             break;
         case INIT_EXPR_TYPE_ARRAY_NEW:
         case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT:
@@ -1317,8 +1340,8 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             }
 
             if (init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) {
-                expr->u.array_new_default.type_index = type_idx;
-                expr->u.array_new_default.length = length;
+                expr->u.unary.v.array_new_default.type_index = type_idx;
+                expr->u.unary.v.array_new_default.length = length;
             }
             else {
                 uint32 i, elem_size, elem_data_count;
@@ -1329,7 +1352,7 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
                     return false;
                 }
                 free_if_fail = true;
-                expr->u.data = init_values;
+                expr->u.unary.v.data = init_values;
 
                 init_values->type_idx = type_idx;
                 init_values->length = length;
@@ -1357,6 +1380,34 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             break;
         }
 #endif /* end of WASM_ENABLE_GC != 0 */
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+        case INIT_EXPR_TYPE_I32_ADD:
+        case INIT_EXPR_TYPE_I32_SUB:
+        case INIT_EXPR_TYPE_I32_MUL:
+        case INIT_EXPR_TYPE_I64_ADD:
+        case INIT_EXPR_TYPE_I64_SUB:
+        case INIT_EXPR_TYPE_I64_MUL:
+        {
+            expr->u.binary.l_expr = expr->u.binary.r_expr = NULL;
+            if (!(expr->u.binary.l_expr =
+                      loader_malloc(sizeof(InitializerExpression), error_buf,
+                                    error_buf_size))) {
+                goto fail;
+            }
+            if (!load_init_expr(&buf, buf_end, module, expr->u.binary.l_expr,
+                                error_buf, error_buf_size))
+                goto fail;
+            if (!(expr->u.binary.r_expr =
+                      loader_malloc(sizeof(InitializerExpression), error_buf,
+                                    error_buf_size))) {
+                goto fail;
+            }
+            if (!load_init_expr(&buf, buf_end, module, expr->u.binary.r_expr,
+                                error_buf, error_buf_size))
+                goto fail;
+            break;
+        }
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */
         default:
             set_error_buf(error_buf, error_buf_size, "invalid init expr type.");
             return false;
@@ -1369,10 +1420,13 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
 fail:
 #if WASM_ENABLE_GC != 0
     if (free_if_fail) {
-        wasm_runtime_free(expr->u.data);
+        wasm_runtime_free(expr->u.unary.v.data);
     }
 #else
     (void)free_if_fail;
+#endif
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    destroy_init_expr(expr);
 #endif
     return false;
 }
@@ -1535,14 +1589,16 @@ load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end,
     /* Create each table data segment */
     for (i = 0; i < module->table_init_data_count; i++) {
         uint32 mode, elem_type;
-        uint32 table_index, init_expr_type, value_count;
-        uint64 init_expr_value, size1;
+        uint32 table_index, value_count;
+        uint64 size1;
+        InitializerExpression offset_expr;
 
         read_uint32(buf, buf_end, mode);
         read_uint32(buf, buf_end, elem_type);
         read_uint32(buf, buf_end, table_index);
-        read_uint32(buf, buf_end, init_expr_type);
-        read_uint64(buf, buf_end, init_expr_value);
+        if (!load_init_expr(&buf, buf_end, module, &offset_expr, error_buf,
+                            error_buf_size))
+            return false;
 #if WASM_ENABLE_GC != 0
         if (wasm_is_type_multi_byte_type(elem_type)) {
             uint16 ref_type, nullable;
@@ -1588,8 +1644,7 @@ load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end,
             }
         }
 #endif
-        data_list[i]->offset.init_expr_type = (uint8)init_expr_type;
-        data_list[i]->offset.u.i64 = (int64)init_expr_value;
+        data_list[i]->offset = offset_expr;
         data_list[i]->value_count = value_count;
         for (j = 0; j < data_list[i]->value_count; j++) {
             if (!load_init_expr(&buf, buf_end, module,
@@ -4500,7 +4555,7 @@ aot_unload(AOTModule *module)
         destroy_import_globals(module->import_globals);
 
     if (module->globals) {
-#if WASM_ENABLE_GC != 0
+#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0
         uint32 i;
         for (i = 0; i < module->global_count; i++) {
             destroy_init_expr(&module->globals[i].init_expr);

+ 212 - 106
core/iwasm/aot/aot_runtime.c

@@ -289,18 +289,21 @@ assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module,
     switch (flag) {
         case INIT_EXPR_TYPE_GET_GLOBAL:
         {
-            if (!check_global_init_expr(module, init_expr->u.global_index,
+            if (!check_global_init_expr(module,
+                                        init_expr->u.unary.v.global_index,
                                         error_buf, error_buf_size)) {
                 return false;
             }
-            if (init_expr->u.global_index < module->import_global_count) {
+            if (init_expr->u.unary.v.global_index
+                < module->import_global_count) {
                 PUT_REF_TO_ADDR(
-                    addr, module->import_globals[init_expr->u.global_index]
-                              .global_data_linked.gc_obj);
+                    addr,
+                    module->import_globals[init_expr->u.unary.v.global_index]
+                        .global_data_linked.gc_obj);
             }
             else {
-                uint32 global_idx =
-                    init_expr->u.global_index - module->import_global_count;
+                uint32 global_idx = init_expr->u.unary.v.global_index
+                                    - module->import_global_count;
                 return assign_table_init_value(
                     module_inst, module, &module->globals[global_idx].init_expr,
                     addr, error_buf, error_buf_size);
@@ -316,7 +319,7 @@ assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module,
         case INIT_EXPR_TYPE_FUNCREF_CONST:
         {
             WASMFuncObjectRef func_obj = NULL;
-            uint32 func_idx = init_expr->u.u32;
+            uint32 func_idx = init_expr->u.unary.v.u32;
 
             if (func_idx != UINT32_MAX) {
                 if (!(func_obj =
@@ -331,7 +334,8 @@ assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module,
         }
         case INIT_EXPR_TYPE_I31_NEW:
         {
-            WASMI31ObjectRef i31_obj = wasm_i31_obj_new(init_expr->u.i32);
+            WASMI31ObjectRef i31_obj =
+                wasm_i31_obj_new(init_expr->u.unary.v.i32);
             PUT_REF_TO_ADDR(addr, i31_obj);
             break;
         }
@@ -345,11 +349,12 @@ assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module,
             uint32 type_idx;
 
             if (flag == INIT_EXPR_TYPE_STRUCT_NEW) {
-                init_values = (WASMStructNewInitValues *)init_expr->u.data;
+                init_values =
+                    (WASMStructNewInitValues *)init_expr->u.unary.v.data;
                 type_idx = init_values->type_idx;
             }
             else {
-                type_idx = init_expr->u.type_index;
+                type_idx = init_expr->u.unary.v.type_index;
             }
 
             struct_type = (WASMStructType *)module->types[type_idx];
@@ -398,12 +403,13 @@ assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module,
             uint32 type_idx, len;
 
             if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) {
-                type_idx = init_expr->u.array_new_default.type_index;
-                len = init_expr->u.array_new_default.length;
+                type_idx = init_expr->u.unary.v.array_new_default.type_index;
+                len = init_expr->u.unary.v.array_new_default.length;
                 arr_init_val = &empty_val;
             }
             else {
-                init_values = (WASMArrayNewInitValues *)init_expr->u.data;
+                init_values =
+                    (WASMArrayNewInitValues *)init_expr->u.unary.v.data;
                 type_idx = init_values->type_idx;
                 len = init_values->length;
 
@@ -454,6 +460,90 @@ assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module,
 }
 #endif /* end of WASM_ENABLE_GC != 0 */
 
+static bool
+get_init_value_recursive(AOTModuleInstance *module_inst, AOTModule *module,
+                         InitializerExpression *expr, WASMValue *value,
+                         char *error_buf, uint32 error_buf_size)
+{
+    uint8 flag = expr->init_expr_type;
+    switch (flag) {
+        case INIT_EXPR_TYPE_GET_GLOBAL:
+        {
+            if (!check_global_init_expr(module, expr->u.unary.v.global_index,
+                                        error_buf, error_buf_size)) {
+                return false;
+            }
+#if WASM_ENABLE_GC == 0
+            *value = module->import_globals[expr->u.unary.v.global_index]
+                         .global_data_linked;
+#else
+            if (expr->u.unary.v.global_index < module->import_global_count) {
+                *value = module->import_globals[expr->u.unary.v.global_index]
+                             .global_data_linked;
+            }
+            else {
+                *value = module
+                             ->globals[expr->u.unary.v.global_index
+                                       - module->import_global_count]
+                             .init_expr.u.unary.v;
+            }
+#endif
+            break;
+        }
+        case INIT_EXPR_TYPE_I32_CONST:
+        case INIT_EXPR_TYPE_I64_CONST:
+        {
+            *value = expr->u.unary.v;
+            break;
+        }
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+        case INIT_EXPR_TYPE_I32_ADD:
+        case INIT_EXPR_TYPE_I32_SUB:
+        case INIT_EXPR_TYPE_I32_MUL:
+        case INIT_EXPR_TYPE_I64_ADD:
+        case INIT_EXPR_TYPE_I64_SUB:
+        case INIT_EXPR_TYPE_I64_MUL:
+        {
+            WASMValue l_value, r_value;
+            if (!get_init_value_recursive(module_inst, module,
+                                          expr->u.binary.l_expr, &l_value,
+                                          error_buf, error_buf_size)) {
+                return false;
+            }
+            if (!get_init_value_recursive(module_inst, module,
+                                          expr->u.binary.r_expr, &r_value,
+                                          error_buf, error_buf_size)) {
+                return false;
+            }
+
+            if (flag == INIT_EXPR_TYPE_I32_ADD) {
+                value->i32 = l_value.i32 + r_value.i32;
+            }
+            else if (flag == INIT_EXPR_TYPE_I32_SUB) {
+                value->i32 = l_value.i32 - r_value.i32;
+            }
+            else if (flag == INIT_EXPR_TYPE_I32_MUL) {
+                value->i32 = l_value.i32 * r_value.i32;
+            }
+            else if (flag == INIT_EXPR_TYPE_I64_ADD) {
+                value->i64 = l_value.i64 + r_value.i64;
+            }
+            else if (flag == INIT_EXPR_TYPE_I64_SUB) {
+                value->i64 = l_value.i64 - r_value.i64;
+            }
+            else if (flag == INIT_EXPR_TYPE_I64_MUL) {
+                value->i64 = l_value.i64 * r_value.i64;
+            }
+            break;
+        }
+#endif
+        default:
+            return false;
+    }
+
+    return true;
+}
+
 static bool
 global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
                    char *error_buf, uint32 error_buf_size)
@@ -482,30 +572,24 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
         flag = init_expr->init_expr_type;
         switch (flag) {
             case INIT_EXPR_TYPE_GET_GLOBAL:
+            case INIT_EXPR_TYPE_I32_CONST:
+            case INIT_EXPR_TYPE_I64_CONST:
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            case INIT_EXPR_TYPE_I32_ADD:
+            case INIT_EXPR_TYPE_I32_SUB:
+            case INIT_EXPR_TYPE_I32_MUL:
+            case INIT_EXPR_TYPE_I64_ADD:
+            case INIT_EXPR_TYPE_I64_SUB:
+            case INIT_EXPR_TYPE_I64_MUL:
+#endif
             {
-                if (!check_global_init_expr(module, init_expr->u.global_index,
-                                            error_buf, error_buf_size)) {
+                WASMValue value;
+                if (!get_init_value_recursive(module_inst, module, init_expr,
+                                              &value, error_buf,
+                                              error_buf_size)) {
                     return false;
                 }
-#if WASM_ENABLE_GC == 0
-                init_global_data(
-                    p, global->type.val_type,
-                    &module->import_globals[init_expr->u.global_index]
-                         .global_data_linked);
-#else
-                if (init_expr->u.global_index < module->import_global_count) {
-                    init_global_data(
-                        p, global->type.val_type,
-                        &module->import_globals[init_expr->u.global_index]
-                             .global_data_linked);
-                }
-                else {
-                    uint32 global_idx =
-                        init_expr->u.global_index - module->import_global_count;
-                    init_global_data(p, global->type.val_type,
-                                     &module->globals[global_idx].init_expr.u);
-                }
-#endif
+                init_global_data(p, global->type.val_type, &value);
                 break;
             }
 #if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0
@@ -526,7 +610,7 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
             case INIT_EXPR_TYPE_FUNCREF_CONST:
             {
                 WASMFuncObjectRef func_obj = NULL;
-                uint32 func_idx = init_expr->u.u32;
+                uint32 func_idx = init_expr->u.unary.v.ref_index;
 
                 if (func_idx != UINT32_MAX) {
                     if (!(func_obj =
@@ -541,7 +625,8 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
             }
             case INIT_EXPR_TYPE_I31_NEW:
             {
-                WASMI31ObjectRef i31_obj = wasm_i31_obj_new(init_expr->u.i32);
+                WASMI31ObjectRef i31_obj =
+                    wasm_i31_obj_new(init_expr->u.unary.v.i32);
                 PUT_REF_TO_ADDR(p, i31_obj);
                 break;
             }
@@ -555,11 +640,12 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
                 uint32 type_idx;
 
                 if (flag == INIT_EXPR_TYPE_STRUCT_NEW) {
-                    init_values = (WASMStructNewInitValues *)init_expr->u.data;
+                    init_values =
+                        (WASMStructNewInitValues *)init_expr->u.unary.v.data;
                     type_idx = init_values->type_idx;
                 }
                 else {
-                    type_idx = init_expr->u.type_index;
+                    type_idx = init_expr->u.unary.v.type_index;
                 }
 
                 struct_type = (WASMStructType *)module->types[type_idx];
@@ -609,12 +695,14 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
                 uint32 type_idx, len;
 
                 if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) {
-                    type_idx = init_expr->u.array_new_default.type_index;
-                    len = init_expr->u.array_new_default.length;
+                    type_idx =
+                        init_expr->u.unary.v.array_new_default.type_index;
+                    len = init_expr->u.unary.v.array_new_default.length;
                     arr_init_val = &empty_val;
                 }
                 else {
-                    init_values = (WASMArrayNewInitValues *)init_expr->u.data;
+                    init_values =
+                        (WASMArrayNewInitValues *)init_expr->u.unary.v.data;
                     type_idx = init_values->type_idx;
                     len = init_values->length;
 
@@ -660,7 +748,8 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
 #endif /* end of WASM_ENABLE_GC != 0 */
             default:
             {
-                init_global_data(p, global->type.val_type, &init_expr->u);
+                init_global_data(p, global->type.val_type,
+                                 &init_expr->u.unary.v);
                 break;
             }
         }
@@ -681,6 +770,7 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
     uint64 total_size;
     AOTTableInitData *table_seg;
     AOTTableInstance *tbl_inst = first_tbl_inst;
+    uint8 offset_flag;
 
     total_size = (uint64)sizeof(AOTTableInstance *) * module_inst->table_count;
     if (total_size > 0
@@ -753,28 +843,25 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
         tbl_inst = module_inst->tables[table_seg->table_index];
         bh_assert(tbl_inst);
 
+        offset_flag = table_seg->offset.init_expr_type;
+
 #if WASM_ENABLE_REF_TYPES != 0
-        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
-            || table_seg->offset.init_expr_type == INIT_EXPR_TYPE_FUNCREF_CONST
-            || table_seg->offset.init_expr_type
-                   == INIT_EXPR_TYPE_REFNULL_CONST);
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || offset_flag == INIT_EXPR_TYPE_FUNCREF_CONST
+                  || offset_flag == INIT_EXPR_TYPE_REFNULL_CONST
+                  || (tbl_inst->is_table64 ? is_valid_i64_offset(offset_flag)
+                                           : is_valid_i32_offset(offset_flag)));
 #else
-        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);
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || (tbl_inst->is_table64 ? is_valid_i64_offset(offset_flag)
+                                           : is_valid_i32_offset(offset_flag)));
 #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;
+        if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) {
+            global_index = table_seg->offset.u.unary.v.global_index;
 
             if (!check_global_init_expr(module, global_index, error_buf,
                                         error_buf_size)) {
@@ -792,8 +879,15 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
             base_offset =
                 *(uint32 *)(module_inst->global_data + global_data_offset);
         }
-        else
-            base_offset = (uint32)table_seg->offset.u.i32;
+        else {
+            WASMValue offset_value;
+            if (!get_init_value_recursive(module_inst, module,
+                                          &table_seg->offset, &offset_value,
+                                          error_buf, error_buf_size)) {
+                return false;
+            }
+            base_offset = (uint32)offset_value.i32;
+        }
 
         /* Copy table data */
         /* base_offset only since length might negative */
@@ -828,7 +922,7 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module,
 #if WASM_ENABLE_GC == 0
         for (j = 0; j < length; j++) {
             tbl_inst->elems[base_offset + j] =
-                table_seg->init_values[j].u.ref_index;
+                table_seg->init_values[j].u.unary.v.ref_index;
         }
 #endif
     }
@@ -1128,6 +1222,7 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent,
     AOTMemInitData *data_seg;
     uint64 total_size;
     mem_offset_t base_offset;
+    uint8 offset_flag;
 
     module_inst->memory_count = memory_count;
     total_size = sizeof(AOTMemoryInstance *) * (uint64)memory_count;
@@ -1166,15 +1261,15 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent,
                initialized */
             continue;
 
-        bh_assert(data_seg->offset.init_expr_type
-                      == (memory_inst->is_memory64 ? INIT_EXPR_TYPE_I64_CONST
-                                                   : INIT_EXPR_TYPE_I32_CONST)
-                  || data_seg->offset.init_expr_type
-                         == INIT_EXPR_TYPE_GET_GLOBAL);
+        offset_flag = data_seg->offset.init_expr_type;
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || (memory_inst->is_memory64
+                          ? is_valid_i64_offset(offset_flag)
+                          : is_valid_i32_offset(offset_flag)));
 
         /* Resolve memory data base offset */
-        if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
-            global_index = data_seg->offset.u.global_index;
+        if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) {
+            global_index = data_seg->offset.u.unary.v.global_index;
 
             if (!check_global_init_expr(module, global_index, error_buf,
                                         error_buf_size)) {
@@ -1202,14 +1297,20 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent,
             }
         }
         else {
+            WASMValue offset_value;
+            if (!get_init_value_recursive(module_inst, module,
+                                          &data_seg->offset, &offset_value,
+                                          error_buf, error_buf_size)) {
+                return false;
+            }
 #if WASM_ENABLE_MEMORY64 != 0
             if (memory_inst->is_memory64) {
-                base_offset = data_seg->offset.u.i64;
+                base_offset = offset_value.i64;
             }
             else
 #endif
             {
-                base_offset = data_seg->offset.u.u32;
+                base_offset = offset_value.u32;
             }
         }
 
@@ -2055,6 +2156,7 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent,
         uint8 tbl_elem_type;
         uint32 tbl_init_size, tbl_max_size, j;
         WASMRefType *tbl_elem_ref_type;
+        WASMValue offset_value;
 
         bh_assert(table_init_data);
 
@@ -2086,69 +2188,73 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent,
         if (!wasm_elem_is_active(table_init_data->mode)) {
             continue;
         }
-
-        bh_assert(table_init_data->offset.init_expr_type
-                      == INIT_EXPR_TYPE_I32_CONST
-                  || table_init_data->offset.init_expr_type
-                         == INIT_EXPR_TYPE_GET_GLOBAL
-                  || table_init_data->offset.init_expr_type
-                         == INIT_EXPR_TYPE_FUNCREF_CONST
-                  || table_init_data->offset.init_expr_type
-                         == INIT_EXPR_TYPE_REFNULL_CONST);
+        uint8 offset_flag = table_init_data->offset.init_expr_type;
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || offset_flag == INIT_EXPR_TYPE_FUNCREF_CONST
+                  || offset_flag == INIT_EXPR_TYPE_REFNULL_CONST
+                  || offset_flag == INIT_EXPR_TYPE_I32_CONST
+                  || offset_flag == INIT_EXPR_TYPE_I32_ADD
+                  || offset_flag == INIT_EXPR_TYPE_I32_SUB
+                  || offset_flag == INIT_EXPR_TYPE_I32_MUL);
 
         /* init vec(funcidx) or vec(expr) */
-        if (table_init_data->offset.init_expr_type
-            == INIT_EXPR_TYPE_GET_GLOBAL) {
+        if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) {
             uint32 data_offset;
-            if (!check_global_init_expr(module,
-                                        table_init_data->offset.u.global_index,
-                                        error_buf, error_buf_size)) {
+            if (!check_global_init_expr(
+                    module, table_init_data->offset.u.unary.v.global_index,
+                    error_buf, error_buf_size)) {
                 goto fail;
             }
 
-            if (table_init_data->offset.u.global_index
+            if (table_init_data->offset.u.unary.v.global_index
                 < module->import_global_count) {
-                data_offset =
-                    module
-                        ->import_globals[table_init_data->offset.u.global_index]
-                        .data_offset;
+                data_offset = module
+                                  ->import_globals[table_init_data->offset.u
+                                                       .unary.v.global_index]
+                                  .data_offset;
             }
             else {
                 data_offset =
                     module
-                        ->globals[table_init_data->offset.u.global_index
+                        ->globals[table_init_data->offset.u.unary.v.global_index
                                   - module->import_global_count]
                         .data_offset;
             }
-
-            table_init_data->offset.u.i32 =
+            offset_value.i32 =
                 *(uint32 *)(module_inst->global_data + data_offset);
         }
+        else {
+            if (!get_init_value_recursive(
+                    module_inst, module, &table_init_data->offset,
+                    &offset_value, error_buf, error_buf_size)) {
+                goto fail;
+            }
+        }
 
         /* check offset since length might negative */
-        if ((uint32)table_init_data->offset.u.i32 > table->cur_size) {
-            LOG_DEBUG("base_offset(%d) > table->cur_size(%d)",
-                      table_init_data->offset.u.i32, table->cur_size);
+        if ((uint32)offset_value.i32 > table->cur_size) {
+            LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", offset_value.i32,
+                      table->cur_size);
             set_error_buf(error_buf, error_buf_size,
                           "out of bounds table access");
             goto fail;
         }
 
-        if ((uint32)table_init_data->offset.u.i32 + table_init_data->value_count
+        if ((uint32)offset_value.i32 + table_init_data->value_count
             > table->cur_size) {
             LOG_DEBUG("base_offset(%d) + length(%d) > table->cur_size(%d)",
-                      table_init_data->offset.u.i32,
-                      table_init_data->value_count, table->cur_size);
+                      offset_value.i32, table_init_data->value_count,
+                      table->cur_size);
             set_error_buf(error_buf, error_buf_size,
                           "out of bounds table access");
             goto fail;
         }
 
         for (j = 0; j < module->table_init_data_list[i]->value_count; j++) {
-            if (!assign_table_init_value(
-                    module_inst, module, &table_init_data->init_values[j],
-                    table_data + table_init_data->offset.u.i32 + j, error_buf,
-                    error_buf_size)) {
+            if (!assign_table_init_value(module_inst, module,
+                                         &table_init_data->init_values[j],
+                                         table_data + offset_value.i32 + j,
+                                         error_buf, error_buf_size)) {
                 goto fail;
             }
         }
@@ -3741,10 +3847,10 @@ aot_table_init(AOTModuleInstance *module_inst, uint32 tbl_idx,
     for (i = 0; i < length; i++) {
 #if WASM_ENABLE_GC != 0
         /* UINT32_MAX indicates that it is a null ref */
-        if (init_values[i].u.ref_index != UINT32_MAX) {
-            if (!(func_obj = aot_create_func_obj(module_inst,
-                                                 init_values[i].u.ref_index,
-                                                 true, NULL, 0))) {
+        if (init_values[i].u.unary.v.ref_index != UINT32_MAX) {
+            if (!(func_obj = aot_create_func_obj(
+                      module_inst, init_values[i].u.unary.v.ref_index, true,
+                      NULL, 0))) {
                 aot_set_exception_with_id(module_inst, EXCE_NULL_FUNC_OBJ);
                 return;
             }
@@ -3754,7 +3860,7 @@ aot_table_init(AOTModuleInstance *module_inst, uint32 tbl_idx,
             table_elems[i] = NULL_REF;
         }
 #else
-        table_elems[i] = init_values[i].u.ref_index;
+        table_elems[i] = init_values[i].u.unary.v.ref_index;
 #endif
     }
 }

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

@@ -225,3 +225,18 @@ read_leb(uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, bool sign,
             return false;
     }
 }
+
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+void
+destroy_init_expr_recursive(InitializerExpression *expr)
+{
+    if (expr == NULL) {
+        return;
+    }
+    if (is_expr_binary_op(expr->init_expr_type)) {
+        destroy_init_expr_recursive(expr->u.binary.l_expr);
+        destroy_init_expr_recursive(expr->u.binary.r_expr);
+    }
+    wasm_runtime_free(expr);
+}
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */

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

@@ -50,6 +50,11 @@ void
 wasm_loader_set_error_buf(char *error_buf, uint32 error_buf_size,
                           const char *string, bool is_aot);
 
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+void
+destroy_init_expr_recursive(InitializerExpression *expr);
+#endif
+
 #ifdef __cplusplus
 }
 #endif

+ 62 - 21
core/iwasm/compilation/aot_emit_aot_file.c

@@ -216,7 +216,7 @@ get_init_expr_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data,
 #if WASM_ENABLE_GC != 0
     WASMModule *module = comp_data->wasm_module;
 #endif
-
+    bh_assert(expr != NULL);
     /* + init value size */
     switch (expr->init_expr_type) {
         case INIT_EXPR_NONE:
@@ -248,7 +248,7 @@ get_init_expr_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data,
         {
             uint32 i;
             WASMStructNewInitValues *struct_new_init_values =
-                (WASMStructNewInitValues *)expr->u.data;
+                (WASMStructNewInitValues *)expr->u.unary.v.data;
 
             /* type_index + field_count + fields */
             size += sizeof(uint32) + sizeof(uint32);
@@ -285,7 +285,7 @@ get_init_expr_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data,
         case INIT_EXPR_TYPE_ARRAY_NEW_FIXED:
         {
             WASMArrayNewInitValues *array_new_init_values =
-                (WASMArrayNewInitValues *)expr->u.data;
+                (WASMArrayNewInitValues *)expr->u.unary.v.data;
             WASMArrayType *array_type = NULL;
             uint32 value_count;
 
@@ -308,6 +308,21 @@ get_init_expr_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data,
             break;
         }
 #endif /* end of WASM_ENABLE_GC != 0 */
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+        case INIT_EXPR_TYPE_I32_ADD:
+        case INIT_EXPR_TYPE_I32_SUB:
+        case INIT_EXPR_TYPE_I32_MUL:
+        case INIT_EXPR_TYPE_I64_ADD:
+        case INIT_EXPR_TYPE_I64_SUB:
+        case INIT_EXPR_TYPE_I64_MUL:
+        {
+            size +=
+                get_init_expr_size(comp_ctx, comp_data, expr->u.binary.l_expr);
+            size +=
+                get_init_expr_size(comp_ctx, comp_data, expr->u.binary.r_expr);
+            break;
+        }
+#endif
         default:
             bh_assert(0);
     }
@@ -324,15 +339,16 @@ get_table_init_data_size(AOTCompContext *comp_ctx,
     /*
      * mode (4 bytes), elem_type (4 bytes)
      *
-     * table_index(4 bytes) + init expr type (4 bytes) + init expr value (8
-     * bytes)
+     * table_index(4 bytes)
      */
-    size = (uint32)(sizeof(uint32) * 2 + sizeof(uint32) + sizeof(uint32)
-                    + sizeof(uint64))
+    size = (uint32)(sizeof(uint32) * 2 + sizeof(uint32))
            /* Size of WasmRefType - inner padding (ref type + nullable +
               heap_type) */
            + 8;
 
+    size += get_init_expr_size(comp_ctx, comp_ctx->comp_data,
+                               &table_init_data->offset);
+
     /* + value count/func index count (4 bytes) + init_values */
     size += sizeof(uint32);
     for (i = 0; i < table_init_data->value_count; i++) {
@@ -1811,6 +1827,10 @@ static bool
 aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
                    AOTCompContext *comp_ctx, InitializerExpression *expr)
 {
+    if (expr == NULL) {
+        aot_set_last_error("invalid init expr.");
+        return false;
+    }
     uint32 offset = *p_offset;
 #if WASM_ENABLE_GC != 0
     WASMModule *module = comp_ctx->comp_data->wasm_module;
@@ -1824,31 +1844,31 @@ aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
             break;
         case INIT_EXPR_TYPE_I32_CONST:
         case INIT_EXPR_TYPE_F32_CONST:
-            EMIT_U32(expr->u.i32);
+            EMIT_U32(expr->u.unary.v.i32);
             break;
         case INIT_EXPR_TYPE_I64_CONST:
         case INIT_EXPR_TYPE_F64_CONST:
-            EMIT_U64(expr->u.i64);
+            EMIT_U64(expr->u.unary.v.i64);
             break;
         case INIT_EXPR_TYPE_V128_CONST:
-            EMIT_V128(expr->u.v128);
+            EMIT_V128(expr->u.unary.v.v128);
             break;
         case INIT_EXPR_TYPE_GET_GLOBAL:
-            EMIT_U32(expr->u.global_index);
+            EMIT_U32(expr->u.unary.v.global_index);
             break;
         case INIT_EXPR_TYPE_FUNCREF_CONST:
         case INIT_EXPR_TYPE_REFNULL_CONST:
-            EMIT_U32(expr->u.ref_index);
+            EMIT_U32(expr->u.unary.v.ref_index);
             break;
 #if WASM_ENABLE_GC != 0
         case INIT_EXPR_TYPE_I31_NEW:
-            EMIT_U32(expr->u.i32);
+            EMIT_U32(expr->u.unary.v.i32);
             break;
         case INIT_EXPR_TYPE_STRUCT_NEW:
         {
             uint32 i;
             WASMStructNewInitValues *init_values =
-                (WASMStructNewInitValues *)expr->u.data;
+                (WASMStructNewInitValues *)expr->u.unary.v.data;
             WASMStructType *struct_type = NULL;
 
             EMIT_U32(init_values->type_idx);
@@ -1879,7 +1899,7 @@ aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
             break;
         }
         case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT:
-            EMIT_U32(expr->u.type_index);
+            EMIT_U32(expr->u.unary.v.type_index);
             break;
         case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT:
         {
@@ -1889,11 +1909,11 @@ aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
                       < module->type_count);
             array_type =
                 (WASMArrayType *)
-                    module->types[expr->u.array_new_default.type_index];
+                    module->types[expr->u.unary.v.array_new_default.type_index];
 
             EMIT_U32(array_type->elem_type);
-            EMIT_U32(expr->u.array_new_default.type_index);
-            EMIT_U32(expr->u.array_new_default.length);
+            EMIT_U32(expr->u.unary.v.array_new_default.type_index);
+            EMIT_U32(expr->u.unary.v.array_new_default.length);
             break;
         }
         case INIT_EXPR_TYPE_ARRAY_NEW:
@@ -1901,7 +1921,7 @@ aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
         {
             uint32 value_count, i, field_size;
             WASMArrayNewInitValues *init_values =
-                (WASMArrayNewInitValues *)expr->u.data;
+                (WASMArrayNewInitValues *)expr->u.unary.v.data;
             WASMArrayType *array_type = NULL;
 
             bh_assert(init_values->type_idx < module->type_count);
@@ -1933,6 +1953,25 @@ aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
             break;
         }
 #endif /* end of WASM_ENABLE_GC != 0 */
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+        case INIT_EXPR_TYPE_I32_ADD:
+        case INIT_EXPR_TYPE_I32_SUB:
+        case INIT_EXPR_TYPE_I32_MUL:
+        case INIT_EXPR_TYPE_I64_ADD:
+        case INIT_EXPR_TYPE_I64_SUB:
+        case INIT_EXPR_TYPE_I64_MUL:
+            if (comp_ctx->enable_extended_const) {
+                if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx,
+                                        expr->u.binary.l_expr)) {
+                    return false;
+                }
+                if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx,
+                                        expr->u.binary.r_expr)) {
+                    return false;
+                }
+            }
+            break;
+#endif
         default:
             aot_set_last_error("invalid init expr type.");
             return false;
@@ -2034,8 +2073,10 @@ aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
         EMIT_U32(init_datas[i]->mode);
         EMIT_U32(init_datas[i]->elem_type);
         EMIT_U32(init_datas[i]->table_index);
-        EMIT_U32(init_datas[i]->offset.init_expr_type);
-        EMIT_U64(init_datas[i]->offset.u.i64);
+        if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx,
+                                &init_datas[i]->offset))
+            return false;
+
 #if WASM_ENABLE_GC != 0
         if (comp_ctx->enable_gc && init_datas[i]->elem_ref_type) {
             EMIT_U16(init_datas[i]->elem_ref_type->ref_ht_common.ref_type);

+ 3 - 0
core/iwasm/compilation/aot_llvm.c

@@ -2787,6 +2787,9 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
     if (option->enable_shared_chain)
         comp_ctx->enable_shared_chain = true;
 
+    if (option->enable_extended_const)
+        comp_ctx->enable_extended_const = true;
+
     comp_ctx->opt_level = option->opt_level;
     comp_ctx->size_level = option->size_level;
 

+ 3 - 0
core/iwasm/compilation/aot_llvm.h

@@ -461,6 +461,9 @@ typedef struct AOTCompContext {
     /* Enable LLVM PGO (Profile-Guided Optimization) */
     bool enable_llvm_pgo;
 
+    /* Enable extended constant expression */
+    bool enable_extended_const;
+
     /* Treat unknown import function as wasm-c-api import function
        and allow to directly invoke it from AOT/JIT code */
     bool quick_invoke_c_api_import;

+ 2 - 1
core/iwasm/fast-jit/fe/jit_emit_table.c

@@ -121,7 +121,8 @@ wasm_init_table(WASMModuleInstance *inst, uint32 tbl_idx, uint32 seg_idx,
                               + dst_offset * sizeof(table_elem_type_t));
     init_values = tbl_seg_init_values + src_offset;
     for (i = 0; i < len; i++) {
-        addr[i] = (table_elem_type_t)(uintptr_t)init_values[+i].u.ref_index;
+        addr[i] =
+            (table_elem_type_t)(uintptr_t)init_values[+i].u.unary.v.ref_index;
     }
 
     return 0;

+ 1 - 0
core/iwasm/include/aot_comp_option.h

@@ -68,6 +68,7 @@ typedef struct AOTCompOption {
     bool enable_ref_types;
     bool enable_gc;
     bool enable_aux_stack_check;
+    bool enable_extended_const;
     AOTStackFrameType aux_stack_frame_type;
     AOTCallStackFeatures call_stack_features;
     bool enable_perf_profiling;

+ 39 - 1
core/iwasm/interpreter/wasm.h

@@ -135,6 +135,12 @@ typedef void *table_elem_type_t;
 #define INIT_EXPR_TYPE_F64_CONST 0x44
 #define INIT_EXPR_TYPE_V128_CONST 0xFD
 #define INIT_EXPR_TYPE_GET_GLOBAL 0x23
+#define INIT_EXPR_TYPE_I32_ADD 0x6A
+#define INIT_EXPR_TYPE_I32_SUB 0x6B
+#define INIT_EXPR_TYPE_I32_MUL 0x6C
+#define INIT_EXPR_TYPE_I64_ADD 0x7C
+#define INIT_EXPR_TYPE_I64_SUB 0x7D
+#define INIT_EXPR_TYPE_I64_MUL 0x7E
 #define INIT_EXPR_TYPE_REFNULL_CONST 0xD0
 #define INIT_EXPR_TYPE_FUNCREF_CONST 0xD2
 #define INIT_EXPR_TYPE_STRUCT_NEW 0xD3
@@ -277,9 +283,41 @@ typedef struct InitializerExpression {
     /* type of INIT_EXPR_TYPE_XXX, which is an instruction of
        constant expression */
     uint8 init_expr_type;
-    WASMValue u;
+    union {
+        struct {
+            WASMValue v;
+        } unary;
+        struct {
+            struct InitializerExpression *l_expr;
+            struct InitializerExpression *r_expr;
+        } binary;
+    } u;
 } InitializerExpression;
 
+static inline bool
+is_expr_binary_op(uint8 flag)
+{
+    return flag == INIT_EXPR_TYPE_I32_ADD || flag == INIT_EXPR_TYPE_I32_SUB
+           || flag == INIT_EXPR_TYPE_I32_MUL || flag == INIT_EXPR_TYPE_I64_ADD
+           || flag == INIT_EXPR_TYPE_I64_SUB || flag == INIT_EXPR_TYPE_I64_MUL;
+}
+
+/* check if table or data offset is valid for i32 offset */
+static inline bool
+is_valid_i32_offset(uint8 flag)
+{
+    return flag == INIT_EXPR_TYPE_I32_CONST || flag == INIT_EXPR_TYPE_I32_ADD
+           || flag == INIT_EXPR_TYPE_I32_SUB || flag == INIT_EXPR_TYPE_I32_MUL;
+}
+
+/* check if table or data offset is valid for i64 offset */
+static inline bool
+is_valid_i64_offset(uint8 flag)
+{
+    return flag == INIT_EXPR_TYPE_I64_CONST || flag == INIT_EXPR_TYPE_I64_ADD
+           || flag == INIT_EXPR_TYPE_I64_SUB || flag == INIT_EXPR_TYPE_I64_MUL;
+}
+
 #if WASM_ENABLE_GC != 0
 /**
  * Reference type of (ref null ht) or (ref ht),

+ 6 - 4
core/iwasm/interpreter/wasm_interp_classic.c

@@ -5958,12 +5958,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                                       || init_values[i].init_expr_type
                                              == INIT_EXPR_TYPE_FUNCREF_CONST);
 #if WASM_ENABLE_GC == 0
-                            table_elems[i] =
-                                (table_elem_type_t)init_values[i].u.ref_index;
+                            table_elems[i] = (table_elem_type_t)init_values[i]
+                                                 .u.unary.v.ref_index;
 #else
-                            if (init_values[i].u.ref_index != UINT32_MAX) {
+                            if (init_values[i].u.unary.v.ref_index
+                                != UINT32_MAX) {
                                 if (!(func_obj = wasm_create_func_obj(
-                                          module, init_values[i].u.ref_index,
+                                          module,
+                                          init_values[i].u.unary.v.ref_index,
                                           true, NULL, 0))) {
                                     goto got_exception;
                                 }

+ 6 - 4
core/iwasm/interpreter/wasm_interp_fast.c

@@ -5350,12 +5350,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                                       || init_values[i].init_expr_type
                                              == INIT_EXPR_TYPE_FUNCREF_CONST);
 #if WASM_ENABLE_GC == 0
-                            table_elems[i] =
-                                (table_elem_type_t)init_values[i].u.ref_index;
+                            table_elems[i] = (table_elem_type_t)init_values[i]
+                                                 .u.unary.v.ref_index;
 #else
-                            if (init_values[i].u.ref_index != UINT32_MAX) {
+                            if (init_values[i].u.unary.v.ref_index
+                                != UINT32_MAX) {
                                 if (!(func_obj = wasm_create_func_obj(
-                                          module, init_values[i].u.ref_index,
+                                          module,
+                                          init_values[i].u.unary.v.ref_index,
                                           true, NULL, 0))) {
                                     goto got_exception;
                                 }

+ 304 - 59
core/iwasm/interpreter/wasm_loader.c

@@ -453,6 +453,9 @@ typedef struct InitValue {
     WASMRefType ref_type;
 #endif
     WASMValue value;
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    InitializerExpression *expr;
+#endif
 } InitValue;
 
 typedef struct ConstExprContext {
@@ -477,7 +480,11 @@ push_const_expr_stack(ConstExprContext *ctx, uint8 flag, uint8 type,
 #if WASM_ENABLE_GC != 0
                       WASMRefType *ref_type, uint8 gc_opcode,
 #endif
-                      WASMValue *value, char *error_buf, uint32 error_buf_size)
+                      WASMValue *value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                      InitializerExpression *expr,
+#endif
+                      char *error_buf, uint32 error_buf_size)
 {
     InitValue *cur_value;
 
@@ -503,6 +510,10 @@ push_const_expr_stack(ConstExprContext *ctx, uint8 flag, uint8 type,
     cur_value->flag = flag;
     cur_value->value = *value;
 
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    cur_value->expr = expr;
+#endif
+
 #if WASM_ENABLE_GC != 0
     cur_value->gc_opcode = gc_opcode;
     if (wasm_is_type_multi_byte_type(type)) {
@@ -587,7 +598,11 @@ pop_const_expr_stack(ConstExprContext *ctx, uint8 *p_flag, uint8 type,
 #if WASM_ENABLE_GC != 0
                      WASMRefType *ref_type, uint8 *p_gc_opcode,
 #endif
-                     WASMValue *p_value, char *error_buf, uint32 error_buf_size)
+                     WASMValue *p_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                     InitializerExpression **p_expr,
+#endif
+                     char *error_buf, uint32 error_buf_size)
 {
     InitValue *cur_value;
 
@@ -623,7 +638,10 @@ pop_const_expr_stack(ConstExprContext *ctx, uint8 *p_flag, uint8 type,
     if (p_gc_opcode)
         *p_gc_opcode = cur_value->gc_opcode;
 #endif
-
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    if (p_expr)
+        *p_expr = cur_value->expr;
+#endif
     return true;
 
 #if WASM_ENABLE_GC != 0
@@ -639,7 +657,7 @@ fail:
 }
 
 static void
-destroy_const_expr_stack(ConstExprContext *ctx)
+destroy_const_expr_stack(ConstExprContext *ctx, bool free_exprs)
 {
 #if WASM_ENABLE_GC != 0
     uint32 i;
@@ -654,24 +672,62 @@ destroy_const_expr_stack(ConstExprContext *ctx)
         }
     }
 #endif
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    if (free_exprs) {
+        for (uint32 j = 0; j < ctx->sp; j++) {
+            if (is_expr_binary_op(ctx->stack[j].expr->init_expr_type)) {
+                destroy_init_expr_recursive(ctx->stack[j].expr);
+                ctx->stack[j].expr = NULL;
+            }
+        }
+    }
+#endif
 
     if (ctx->stack != ctx->data) {
         wasm_runtime_free(ctx->stack);
     }
 }
 
-#if WASM_ENABLE_GC != 0
+#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0
 static void
 destroy_init_expr(WASMModule *module, InitializerExpression *expr)
 {
+#if WASM_ENABLE_GC != 0
     if (expr->init_expr_type == INIT_EXPR_TYPE_STRUCT_NEW
         || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW
         || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) {
-        destroy_init_expr_data_recursive(module, expr->u.data);
+        destroy_init_expr_data_recursive(module, expr->u.unary.v.data);
     }
+#endif
+
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    // free left expr and right exprs for binary oprand
+    if (!is_expr_binary_op(expr->init_expr_type)) {
+        return;
+    }
+    if (expr->u.binary.l_expr) {
+        destroy_init_expr_recursive(expr->u.binary.l_expr);
+    }
+    if (expr->u.binary.r_expr) {
+        destroy_init_expr_recursive(expr->u.binary.r_expr);
+    }
+    expr->u.binary.l_expr = expr->u.binary.r_expr = NULL;
+#endif
 }
-#endif /* end of WASM_ENABLE_GC != 0 */
+#endif
 
+/* for init expr
+ *    (data (i32.add (i32.const 0) (i32.sub (i32.const 1) (i32.const 2)))),
+ *   the binary format is
+ *       0x11: 41 00 ; i32.const 0
+ *       0x13: 41 01 ; i32.const 1
+ *       0x15: 41 02 ; i32.const 2
+ *       0x17: 6b    ; i32.sub
+ *       0x18: 6a    ; i32.add
+ *   for traversal: read opcodes and push them onto the stack. When encountering
+ *   a binary opcode, pop two values from the stack which become the left and
+ *  right child nodes of this binary operation node.
+ */
 static bool
 load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                InitializerExpression *init_expr, uint8 type, void *ref_type,
@@ -687,6 +743,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
     uint8 opcode;
     WASMRefType cur_ref_type = { 0 };
 #endif
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    InitializerExpression *cur_expr = NULL;
+#endif
 
     init_const_expr_stack(&const_expr_ctx, module);
 
@@ -699,24 +758,32 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
             case INIT_EXPR_TYPE_I32_CONST:
                 read_leb_int32(p, p_end, cur_value.i32);
 
-                if (!push_const_expr_stack(
-                        &const_expr_ctx, flag, VALUE_TYPE_I32,
+                if (!push_const_expr_stack(&const_expr_ctx, flag,
+                                           VALUE_TYPE_I32,
 #if WASM_ENABLE_GC != 0
-                        NULL, 0,
+                                           NULL, 0,
 #endif
-                        &cur_value, error_buf, error_buf_size))
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
                     goto fail;
                 break;
             /* i64.const */
             case INIT_EXPR_TYPE_I64_CONST:
                 read_leb_int64(p, p_end, cur_value.i64);
 
-                if (!push_const_expr_stack(
-                        &const_expr_ctx, flag, VALUE_TYPE_I64,
+                if (!push_const_expr_stack(&const_expr_ctx, flag,
+                                           VALUE_TYPE_I64,
 #if WASM_ENABLE_GC != 0
-                        NULL, 0,
+                                           NULL, 0,
 #endif
-                        &cur_value, error_buf, error_buf_size))
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
                     goto fail;
                 break;
             /* f32.const */
@@ -726,12 +793,16 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                 for (i = 0; i < sizeof(float32); i++)
                     *p_float++ = *p++;
 
-                if (!push_const_expr_stack(
-                        &const_expr_ctx, flag, VALUE_TYPE_F32,
+                if (!push_const_expr_stack(&const_expr_ctx, flag,
+                                           VALUE_TYPE_F32,
 #if WASM_ENABLE_GC != 0
-                        NULL, 0,
+                                           NULL, 0,
+#endif
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
 #endif
-                        &cur_value, error_buf, error_buf_size))
+                                           error_buf, error_buf_size))
                     goto fail;
                 break;
             /* f64.const */
@@ -741,12 +812,16 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                 for (i = 0; i < sizeof(float64); i++)
                     *p_float++ = *p++;
 
-                if (!push_const_expr_stack(
-                        &const_expr_ctx, flag, VALUE_TYPE_F64,
+                if (!push_const_expr_stack(&const_expr_ctx, flag,
+                                           VALUE_TYPE_F64,
 #if WASM_ENABLE_GC != 0
-                        NULL, 0,
+                                           NULL, 0,
 #endif
-                        &cur_value, error_buf, error_buf_size))
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
                     goto fail;
                 break;
 #if WASM_ENABLE_SIMD != 0
@@ -767,12 +842,16 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                 cur_value.v128.i64x2[0] = high;
                 cur_value.v128.i64x2[1] = low;
 
-                if (!push_const_expr_stack(
-                        &const_expr_ctx, flag, VALUE_TYPE_V128,
+                if (!push_const_expr_stack(&const_expr_ctx, flag,
+                                           VALUE_TYPE_V128,
 #if WASM_ENABLE_GC != 0
-                        NULL, 0,
+                                           NULL, 0,
+#endif
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
 #endif
-                        &cur_value, error_buf, error_buf_size))
+                                           error_buf, error_buf_size))
                     goto fail;
 #if WASM_ENABLE_WAMR_COMPILER != 0
                 /* If any init_expr is v128.const, mark SIMD used */
@@ -783,7 +862,92 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 #endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || \
           (WASM_ENABLE_FAST_INTERP != 0) */
 #endif /* end of WASM_ENABLE_SIMD */
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            case INIT_EXPR_TYPE_I32_ADD:
+            case INIT_EXPR_TYPE_I32_SUB:
+            case INIT_EXPR_TYPE_I32_MUL:
+            case INIT_EXPR_TYPE_I64_ADD:
+            case INIT_EXPR_TYPE_I64_SUB:
+            case INIT_EXPR_TYPE_I64_MUL:
+            {
+
+                InitializerExpression *l_expr, *r_expr;
+                WASMValue l_value, r_value;
+                uint8 l_flag, r_flag;
+                uint8 value_type;
+
+                if (flag == INIT_EXPR_TYPE_I32_ADD
+                    || flag == INIT_EXPR_TYPE_I32_SUB
+                    || flag == INIT_EXPR_TYPE_I32_MUL) {
+                    value_type = VALUE_TYPE_I32;
+                }
+                else {
+                    value_type = VALUE_TYPE_I64;
+                }
 
+                /* If right flag indicates a binary operation, right expr will
+                 * be popped from stack. Otherwise, allocate a new expr for
+                 * right expr. Same for left expr.
+                 */
+                if (!(pop_const_expr_stack(&const_expr_ctx, &r_flag, value_type,
+#if WASM_ENABLE_GC != 0
+                                           NULL, NULL,
+#endif
+                                           &r_value, &r_expr, error_buf,
+                                           error_buf_size))) {
+                    goto fail;
+                }
+                if (!is_expr_binary_op(r_flag)) {
+                    if (!(r_expr = loader_malloc(sizeof(InitializerExpression),
+                                                 error_buf, error_buf_size))) {
+                        goto fail;
+                    }
+                    r_expr->init_expr_type = r_flag;
+                    r_expr->u.unary.v = r_value;
+                }
+
+                if (!(pop_const_expr_stack(&const_expr_ctx, &l_flag, value_type,
+#if WASM_ENABLE_GC != 0
+                                           NULL, NULL,
+#endif
+                                           &l_value, &l_expr, error_buf,
+                                           error_buf_size))) {
+                    destroy_init_expr_recursive(r_expr);
+                    goto fail;
+                }
+                if (!is_expr_binary_op(l_flag)) {
+                    if (!(l_expr = loader_malloc(sizeof(InitializerExpression),
+                                                 error_buf, error_buf_size))) {
+                        destroy_init_expr_recursive(r_expr);
+                        goto fail;
+                    }
+                    l_expr->init_expr_type = l_flag;
+                    l_expr->u.unary.v = l_value;
+                }
+
+                if (!(cur_expr = loader_malloc(sizeof(InitializerExpression),
+                                               error_buf, error_buf_size))) {
+                    destroy_init_expr_recursive(l_expr);
+                    destroy_init_expr_recursive(r_expr);
+                    goto fail;
+                }
+                cur_expr->init_expr_type = flag;
+                cur_expr->u.binary.l_expr = l_expr;
+                cur_expr->u.binary.r_expr = r_expr;
+
+                if (!push_const_expr_stack(&const_expr_ctx, flag, value_type,
+#if WASM_ENABLE_GC != 0
+                                           NULL, 0,
+#endif
+                                           &cur_value, cur_expr, error_buf,
+                                           error_buf_size)) {
+                    destroy_init_expr_recursive(cur_expr);
+                    goto fail;
+                }
+
+                break;
+            }
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR */
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
             /* ref.func */
             case INIT_EXPR_TYPE_FUNCREF_CONST:
@@ -799,6 +963,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 #if WASM_ENABLE_GC == 0
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            VALUE_TYPE_FUNCREF, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
                                            error_buf, error_buf_size))
                     goto fail;
 #else
@@ -816,8 +983,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                                              false, type_idx);
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            cur_ref_type.ref_type, &cur_ref_type,
-                                           0, &cur_value, error_buf,
-                                           error_buf_size))
+                                           0, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
                     goto fail;
 #endif
 #if WASM_ENABLE_WAMR_COMPILER != 0
@@ -837,8 +1007,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                 cur_value.ref_index = NULL_REF;
                 if (!push_const_expr_stack(&const_expr_ctx, flag, type1,
-                                           &cur_value, error_buf,
-                                           error_buf_size))
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
                     goto fail;
 #else
                 int32 heap_type;
@@ -861,13 +1034,19 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                     if (!push_const_expr_stack(&const_expr_ctx, flag,
                                                cur_ref_type.ref_type,
                                                &cur_ref_type, 0, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                               NULL,
+#endif
                                                error_buf, error_buf_size))
                         goto fail;
                 }
                 else {
                     if (!push_const_expr_stack(&const_expr_ctx, flag, type1,
-                                               NULL, 0, &cur_value, error_buf,
-                                               error_buf_size))
+                                               NULL, 0, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                               NULL,
+#endif
+                                               error_buf, error_buf_size))
                         goto fail;
                 }
 #endif
@@ -956,8 +1135,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 #if WASM_ENABLE_GC != 0
                                            &cur_ref_type, 0,
 #endif
-                                           &cur_value, error_buf,
-                                           error_buf_size))
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
                     goto fail;
 
                 break;
@@ -1020,6 +1202,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                                     &const_expr_ctx, NULL, field_type,
                                     field_ref_type, NULL,
                                     &struct_init_values->fields[field_idx],
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                    NULL,
+#endif
                                     error_buf, error_buf_size)) {
                                 destroy_init_expr_data_recursive(
                                     module, struct_init_values);
@@ -1033,6 +1218,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         if (!push_const_expr_stack(
                                 &const_expr_ctx, flag, cur_ref_type.ref_type,
                                 &cur_ref_type, (uint8)opcode1, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                NULL,
+#endif
                                 error_buf, error_buf_size)) {
                             destroy_init_expr_data_recursive(
                                 module, struct_init_values);
@@ -1064,6 +1252,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         if (!push_const_expr_stack(
                                 &const_expr_ctx, flag, cur_ref_type.ref_type,
                                 &cur_ref_type, (uint8)opcode1, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                NULL,
+#endif
                                 error_buf, error_buf_size)) {
                             goto fail;
                         }
@@ -1112,8 +1303,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                                 if (!pop_const_expr_stack(
                                         &const_expr_ctx, NULL, VALUE_TYPE_I32,
-                                        NULL, NULL, &len_val, error_buf,
-                                        error_buf_size)) {
+                                        NULL, NULL, &len_val,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                        NULL,
+#endif
+                                        error_buf, error_buf_size)) {
                                     goto fail;
                                 }
 
@@ -1132,6 +1326,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                                         &const_expr_ctx, NULL, elem_type,
                                         elem_ref_type, NULL,
                                         &array_init_values->elem_data[0],
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                        NULL,
+#endif
                                         error_buf, error_buf_size)) {
                                     destroy_init_expr_data_recursive(
                                         module, array_init_values);
@@ -1164,6 +1361,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                                             elem_ref_type, NULL,
                                             &array_init_values
                                                  ->elem_data[i - 1],
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                            NULL,
+#endif
                                             error_buf, error_buf_size)) {
                                         destroy_init_expr_data_recursive(
                                             module, array_init_values);
@@ -1180,10 +1380,13 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                             uint32 len;
 
                             /* POP(i32) */
-                            if (!pop_const_expr_stack(&const_expr_ctx, NULL,
-                                                      VALUE_TYPE_I32, NULL,
-                                                      NULL, &len_val, error_buf,
-                                                      error_buf_size)) {
+                            if (!pop_const_expr_stack(
+                                    &const_expr_ctx, NULL, VALUE_TYPE_I32, NULL,
+                                    NULL, &len_val,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                    NULL,
+#endif
+                                    error_buf, error_buf_size)) {
                                 goto fail;
                             }
                             len = len_val.i32;
@@ -1197,6 +1400,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         if (!push_const_expr_stack(
                                 &const_expr_ctx, flag, cur_ref_type.ref_type,
                                 &cur_ref_type, (uint8)opcode1, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                NULL,
+#endif
                                 error_buf, error_buf_size)) {
                             if (array_init_values) {
                                 destroy_init_expr_data_recursive(
@@ -1223,9 +1429,13 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                     case WASM_OP_REF_I31:
                     {
                         /* POP(i32) */
-                        if (!pop_const_expr_stack(
-                                &const_expr_ctx, NULL, VALUE_TYPE_I32, NULL,
-                                NULL, &cur_value, error_buf, error_buf_size)) {
+                        if (!pop_const_expr_stack(&const_expr_ctx, NULL,
+                                                  VALUE_TYPE_I32, NULL, NULL,
+                                                  &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                                  NULL,
+#endif
+                                                  error_buf, error_buf_size)) {
                             goto fail;
                         }
 
@@ -1234,6 +1444,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                         if (!push_const_expr_stack(
                                 &const_expr_ctx, flag, cur_ref_type.ref_type,
                                 &cur_ref_type, (uint8)opcode1, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                NULL,
+#endif
                                 error_buf, error_buf_size)) {
                             goto fail;
                         }
@@ -1268,7 +1481,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 #if WASM_ENABLE_GC != 0
                               ref_type, &opcode,
 #endif
-                              &cur_value, error_buf, error_buf_size)) {
+                              &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                              &cur_expr,
+#endif
+                              error_buf, error_buf_size)) {
         goto fail;
     }
 
@@ -1278,8 +1495,21 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
         goto fail;
     }
 
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    if (cur_expr != NULL) {
+        bh_memcpy_s(init_expr, sizeof(InitializerExpression), cur_expr,
+                    sizeof(InitializerExpression));
+        wasm_runtime_free(cur_expr);
+    }
+    else {
+        init_expr->init_expr_type = flag;
+        init_expr->u.unary.v = cur_value;
+    }
+
+#else
     init_expr->init_expr_type = flag;
-    init_expr->u = cur_value;
+    init_expr->u.unary.v = cur_value;
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */
 
 #if WASM_ENABLE_GC != 0
     if (init_expr->init_expr_type == WASM_OP_GC_PREFIX) {
@@ -1310,11 +1540,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 #endif /* end of WASM_ENABLE_GC != 0 */
 
     *p_buf = p;
-    destroy_const_expr_stack(&const_expr_ctx);
+    destroy_const_expr_stack(&const_expr_ctx, false);
     return true;
 
 fail:
-    destroy_const_expr_stack(&const_expr_ctx);
+    destroy_const_expr_stack(&const_expr_ctx, true);
     return false;
 }
 
@@ -4072,9 +4302,9 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
             if (global->init_expr.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
                 uint8 global_type;
                 WASMRefType *global_ref_type;
-                uint32 global_idx = global->init_expr.u.global_index;
+                uint32 global_idx = global->init_expr.u.unary.v.global_index;
 
-                if (global->init_expr.u.global_index
+                if (global->init_expr.u.unary.v.global_index
                     >= module->import_global_count + i) {
                     set_error_buf(error_buf, error_buf_size, "unknown global");
                     return false;
@@ -4471,7 +4701,7 @@ load_func_index_vec(const uint8 **p_buf, const uint8 *buf_end,
         }
 
         init_expr->init_expr_type = INIT_EXPR_TYPE_FUNCREF_CONST;
-        init_expr->u.ref_index = function_index;
+        init_expr->u.unary.v.ref_index = function_index;
     }
 
     *p_buf = p;
@@ -4744,7 +4974,7 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end,
 
 #if WASM_ENABLE_MEMORY64 != 0
             if (table_elem_idx_type == VALUE_TYPE_I64
-                && table_segment->base_offset.u.u64 > UINT32_MAX) {
+                && table_segment->base_offset.u.unary.v.u64 > UINT32_MAX) {
                 set_error_buf(error_buf, error_buf_size,
                               "In table64, table base offset can't be "
                               "larger than UINT32_MAX");
@@ -4904,6 +5134,9 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end,
 
             if (!(dataseg = module->data_segments[i] = loader_malloc(
                       sizeof(WASMDataSeg), error_buf, error_buf_size))) {
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                destroy_init_expr(module, &init_expr);
+#endif
                 return false;
             }
 
@@ -6031,7 +6264,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
                     && global->init_expr.init_expr_type
                            == INIT_EXPR_TYPE_I32_CONST) {
                     aux_heap_base_global = global;
-                    aux_heap_base = (uint64)(uint32)global->init_expr.u.i32;
+                    aux_heap_base =
+                        (uint64)(uint32)global->init_expr.u.unary.v.i32;
                     aux_heap_base_global_index = export->index;
                     LOG_VERBOSE("Found aux __heap_base global, value: %" PRIu64,
                                 aux_heap_base);
@@ -6052,7 +6286,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
                     && global->init_expr.init_expr_type
                            == INIT_EXPR_TYPE_I32_CONST) {
                     aux_data_end_global = global;
-                    aux_data_end = (uint64)(uint32)global->init_expr.u.i32;
+                    aux_data_end =
+                        (uint64)(uint32)global->init_expr.u.unary.v.i32;
                     aux_data_end_global_index = export->index;
                     LOG_VERBOSE("Found aux __data_end global, value: %" PRIu64,
                                 aux_data_end);
@@ -6093,10 +6328,11 @@ load_from_sections(WASMModule *module, WASMSection *sections,
                         && global->type.val_type == VALUE_TYPE_I32
                         && global->init_expr.init_expr_type
                                == INIT_EXPR_TYPE_I32_CONST
-                        && (uint64)(uint32)global->init_expr.u.i32
+                        && (uint64)(uint32)global->init_expr.u.unary.v.i32
                                <= aux_heap_base) {
                         aux_stack_top_global = global;
-                        aux_stack_top = (uint64)(uint32)global->init_expr.u.i32;
+                        aux_stack_top =
+                            (uint64)(uint32)global->init_expr.u.unary.v.i32;
                         module->aux_stack_top_global_index =
                             module->import_global_count + global_index;
                         module->aux_stack_bottom = aux_stack_top;
@@ -6947,7 +7183,7 @@ wasm_loader_unload(WASMModule *module)
         wasm_runtime_free(module->memories);
 
     if (module->globals) {
-#if WASM_ENABLE_GC != 0
+#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0
         for (i = 0; i < module->global_count; i++) {
             destroy_init_expr(module, &module->globals[i].init_expr);
         }
@@ -6980,6 +7216,9 @@ wasm_loader_unload(WASMModule *module)
 #endif
                 wasm_runtime_free(module->table_segments[i].init_values);
             }
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            destroy_init_expr(module, &module->table_segments[i].base_offset);
+#endif
         }
         wasm_runtime_free(module->table_segments);
     }
@@ -6989,6 +7228,10 @@ wasm_loader_unload(WASMModule *module)
             if (module->data_segments[i]) {
                 if (module->data_segments[i]->is_data_cloned)
                     wasm_runtime_free(module->data_segments[i]->data);
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                destroy_init_expr(module,
+                                  &(module->data_segments[i]->base_offset));
+#endif
                 wasm_runtime_free(module->data_segments[i]);
             }
         }
@@ -13260,7 +13503,8 @@ re_scan:
                                 == VALUE_TYPE_FUNCREF
                             && module->globals[i].init_expr.init_expr_type
                                    == INIT_EXPR_TYPE_FUNCREF_CONST
-                            && module->globals[i].init_expr.u.u32 == func_idx) {
+                            && module->globals[i].init_expr.u.unary.v.u32
+                                   == func_idx) {
                             func_declared = true;
                             break;
                         }
@@ -13289,7 +13533,8 @@ re_scan:
 #endif
                             ) {
                                 for (j = 0; j < table_seg->value_count; j++) {
-                                    if (table_seg->init_values[j].u.ref_index
+                                    if (table_seg->init_values[j]
+                                            .u.unary.v.ref_index
                                         == func_idx) {
                                         func_declared = true;
                                         break;

+ 220 - 30
core/iwasm/interpreter/wasm_mini_loader.c

@@ -261,6 +261,9 @@ typedef struct InitValue {
     uint8 type;
     uint8 flag;
     WASMValue value;
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    InitializerExpression *expr;
+#endif
 } InitValue;
 
 typedef struct ConstExprContext {
@@ -282,7 +285,11 @@ init_const_expr_stack(ConstExprContext *ctx, WASMModule *module)
 
 static bool
 push_const_expr_stack(ConstExprContext *ctx, uint8 flag, uint8 type,
-                      WASMValue *value, char *error_buf, uint32 error_buf_size)
+                      WASMValue *value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                      InitializerExpression *expr,
+#endif
+                      char *error_buf, uint32 error_buf_size)
 {
     InitValue *cur_value;
 
@@ -305,6 +312,9 @@ push_const_expr_stack(ConstExprContext *ctx, uint8 flag, uint8 type,
     cur_value->type = type;
     cur_value->flag = flag;
     cur_value->value = *value;
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    cur_value->expr = expr;
+#endif
 
     return true;
 fail:
@@ -313,7 +323,11 @@ fail:
 
 static bool
 pop_const_expr_stack(ConstExprContext *ctx, uint8 *p_flag, uint8 type,
-                     WASMValue *p_value, char *error_buf, uint32 error_buf_size)
+                     WASMValue *p_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                     InitializerExpression **p_expr,
+#endif
+                     char *error_buf, uint32 error_buf_size)
 {
     InitValue *cur_value;
 
@@ -331,18 +345,50 @@ pop_const_expr_stack(ConstExprContext *ctx, uint8 *p_flag, uint8 type,
         *p_flag = cur_value->flag;
     if (p_value)
         *p_value = cur_value->value;
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    if (p_expr)
+        *p_expr = cur_value->expr;
+#endif
 
     return true;
 }
 
 static void
-destroy_const_expr_stack(ConstExprContext *ctx)
+destroy_const_expr_stack(ConstExprContext *ctx, bool free_exprs)
 {
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    if (free_exprs) {
+        for (uint32 j = 0; j < ctx->sp; j++) {
+            if (is_expr_binary_op(ctx->stack[j].expr->init_expr_type)) {
+                destroy_init_expr_recursive(ctx->stack[j].expr);
+                ctx->stack[j].expr = NULL;
+            }
+        }
+    }
+#endif
     if (ctx->stack != ctx->data) {
         wasm_runtime_free(ctx->stack);
     }
 }
 
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+static void
+destroy_init_expr(InitializerExpression *expr)
+{
+    // free left expr and right exprs for binary oprand
+    if (is_expr_binary_op(expr->init_expr_type)) {
+        return;
+    }
+    if (expr->u.binary.l_expr) {
+        destroy_init_expr_recursive(expr->u.binary.l_expr);
+    }
+    if (expr->u.binary.r_expr) {
+        destroy_init_expr_recursive(expr->u.binary.r_expr);
+    }
+    expr->u.binary.l_expr = expr->u.binary.r_expr = NULL;
+}
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */
+
 static bool
 load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                InitializerExpression *init_expr, uint8 type, char *error_buf,
@@ -353,6 +399,9 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
     uint32 i;
     ConstExprContext const_expr_ctx = { 0 };
     WASMValue cur_value = { 0 };
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    InitializerExpression *cur_expr = NULL;
+#endif
 
     init_const_expr_stack(&const_expr_ctx, module);
 
@@ -367,8 +416,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            VALUE_TYPE_I32, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
                                            error_buf, error_buf_size)) {
-                    bh_assert(0);
+                    goto fail;
                 }
                 break;
             /* i64.const */
@@ -377,8 +429,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            VALUE_TYPE_I64, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
                                            error_buf, error_buf_size)) {
-                    bh_assert(0);
+                    goto fail;
                 }
                 break;
             /* f32.const */
@@ -390,8 +445,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            VALUE_TYPE_F32, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
                                            error_buf, error_buf_size)) {
-                    bh_assert(0);
+                    goto fail;
                 }
                 break;
             /* f64.const */
@@ -403,8 +461,11 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            VALUE_TYPE_F64, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
                                            error_buf, error_buf_size)) {
-                    bh_assert(0);
+                    goto fail;
                 }
                 break;
 
@@ -417,13 +478,16 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                 cur_value.ref_index = func_idx;
                 if (!check_function_index(module, func_idx, error_buf,
                                           error_buf_size)) {
-                    bh_assert(0);
+                    goto fail;
                 }
 
                 if (!push_const_expr_stack(&const_expr_ctx, flag,
                                            VALUE_TYPE_FUNCREF, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
                                            error_buf, error_buf_size)) {
-                    bh_assert(0);
+                    goto fail;
                 }
                 break;
             }
@@ -438,9 +502,12 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
                 cur_value.ref_index = UINT32_MAX;
                 if (!push_const_expr_stack(&const_expr_ctx, flag, type1,
-                                           &cur_value, error_buf,
-                                           error_buf_size)) {
-                    bh_assert(0);
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size)) {
+                    goto fail;
                 }
                 break;
             }
@@ -471,15 +538,93 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
                 }
 
                 if (!push_const_expr_stack(&const_expr_ctx, flag, global_type,
-                                           &cur_value, error_buf,
-                                           error_buf_size))
-                    bh_assert(0);
+                                           &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                                           NULL,
+#endif
+                                           error_buf, error_buf_size))
+                    goto fail;
 
                 break;
             }
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            case INIT_EXPR_TYPE_I32_ADD:
+            case INIT_EXPR_TYPE_I64_ADD:
+            case INIT_EXPR_TYPE_I32_SUB:
+            case INIT_EXPR_TYPE_I64_SUB:
+            case INIT_EXPR_TYPE_I32_MUL:
+            case INIT_EXPR_TYPE_I64_MUL:
+            {
+                InitializerExpression *l_expr, *r_expr;
+                WASMValue l_value, r_value;
+                uint8 l_flag, r_flag;
+                uint8 value_type;
+
+                if (flag == INIT_EXPR_TYPE_I32_ADD
+                    || flag == INIT_EXPR_TYPE_I32_SUB
+                    || flag == INIT_EXPR_TYPE_I32_MUL) {
+                    value_type = VALUE_TYPE_I32;
+                }
+                else {
+                    value_type = VALUE_TYPE_I64;
+                }
+
+                /* If right flag indicates a binary operation, right expr will
+                 * be popped from stack. Otherwise, allocate a new expr for
+                 * right expr. Same for left expr.
+                 */
+                if (!(pop_const_expr_stack(&const_expr_ctx, &r_flag, value_type,
+                                           &r_value, &r_expr, error_buf,
+                                           error_buf_size))) {
+                    goto fail;
+                }
+                if (!is_expr_binary_op(r_flag)) {
+                    if (!(r_expr = loader_malloc(sizeof(InitializerExpression),
+                                                 error_buf, error_buf_size))) {
+                        goto fail;
+                    }
+                    r_expr->init_expr_type = r_flag;
+                    r_expr->u.unary.v = r_value;
+                }
+
+                if (!(pop_const_expr_stack(&const_expr_ctx, &l_flag, value_type,
+                                           &l_value, &l_expr, error_buf,
+                                           error_buf_size))) {
+                    destroy_init_expr_recursive(r_expr);
+                    goto fail;
+                }
+                if (!is_expr_binary_op(l_flag)) {
+                    if (!(l_expr = loader_malloc(sizeof(InitializerExpression),
+                                                 error_buf, error_buf_size))) {
+                        destroy_init_expr_recursive(r_expr);
+                        goto fail;
+                    }
+                    l_expr->init_expr_type = l_flag;
+                    l_expr->u.unary.v = l_value;
+                }
+
+                if (!(cur_expr = loader_malloc(sizeof(InitializerExpression),
+                                               error_buf, error_buf_size))) {
+                    destroy_init_expr_recursive(l_expr);
+                    destroy_init_expr_recursive(r_expr);
+                    goto fail;
+                }
+                cur_expr->init_expr_type = flag;
+                cur_expr->u.binary.l_expr = l_expr;
+                cur_expr->u.binary.r_expr = r_expr;
+
+                if (!push_const_expr_stack(&const_expr_ctx, flag, value_type,
+                                           &cur_value, cur_expr, error_buf,
+                                           error_buf_size)) {
+                    destroy_init_expr_recursive(cur_expr);
+                    goto fail;
+                }
+                break;
+            }
+#endif
             default:
             {
-                bh_assert(0);
+                goto fail;
             }
         }
 
@@ -489,18 +634,42 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
 
     /* There should be only one value left on the init value stack */
     if (!pop_const_expr_stack(&const_expr_ctx, &flag, type, &cur_value,
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                              &cur_expr,
+#endif
                               error_buf, error_buf_size)) {
-        bh_assert(0);
+        goto fail;
     }
 
-    bh_assert(const_expr_ctx.sp == 0);
+    if (const_expr_ctx.sp != 0) {
+        set_error_buf(error_buf, error_buf_size,
+                      "type mismatch: illegal constant opcode sequence");
+        goto fail;
+    }
 
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+    if (cur_expr != NULL) {
+        bh_memcpy_s(init_expr, sizeof(InitializerExpression), cur_expr,
+                    sizeof(InitializerExpression));
+        wasm_runtime_free(cur_expr);
+    }
+    else {
+        init_expr->init_expr_type = flag;
+        init_expr->u.unary.v = cur_value;
+    }
+
+#else
     init_expr->init_expr_type = flag;
-    init_expr->u = cur_value;
+    init_expr->u.unary.v = cur_value;
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */
 
     *p_buf = p;
-    destroy_const_expr_stack(&const_expr_ctx);
+    destroy_const_expr_stack(&const_expr_ctx, false);
     return true;
+
+fail:
+    destroy_const_expr_stack(&const_expr_ctx, true);
+    return false;
 }
 
 static bool
@@ -1385,13 +1554,14 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                  * global.get instructions are
                  * only allowed to refer to imported globals.
                  */
-                uint32 target_global_index = global->init_expr.u.global_index;
+                uint32 target_global_index =
+                    global->init_expr.u.unary.v.global_index;
                 bh_assert(target_global_index < module->import_global_count);
                 (void)target_global_index;
             }
             else if (INIT_EXPR_TYPE_FUNCREF_CONST
                      == global->init_expr.init_expr_type) {
-                bh_assert(global->init_expr.u.ref_index
+                bh_assert(global->init_expr.u.unary.v.ref_index
                           < module->import_function_count
                                 + module->function_count);
             }
@@ -1575,7 +1745,7 @@ load_func_index_vec(const uint8 **p_buf, const uint8 *buf_end,
         }
 
         init_expr->init_expr_type = INIT_EXPR_TYPE_FUNCREF_CONST;
-        init_expr->u.ref_index = function_index;
+        init_expr->u.unary.v.ref_index = function_index;
     }
 
     *p_buf = p;
@@ -1890,6 +2060,9 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end,
 
             if (!(dataseg = module->data_segments[i] = loader_malloc(
                       sizeof(WASMDataSeg), error_buf, error_buf_size))) {
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                destroy_init_expr(&init_expr);
+#endif
                 return false;
             }
 
@@ -2778,7 +2951,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
                     && global->init_expr.init_expr_type
                            == INIT_EXPR_TYPE_I32_CONST) {
                     aux_heap_base_global = global;
-                    aux_heap_base = (uint64)(uint32)global->init_expr.u.i32;
+                    aux_heap_base =
+                        (uint64)(uint32)global->init_expr.u.unary.v.i32;
                     aux_heap_base_global_index = export->index;
                     LOG_VERBOSE("Found aux __heap_base global, value: %" PRIu64,
                                 aux_heap_base);
@@ -2798,7 +2972,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
                     && global->init_expr.init_expr_type
                            == INIT_EXPR_TYPE_I32_CONST) {
                     aux_data_end_global = global;
-                    aux_data_end = (uint64)(uint32)global->init_expr.u.i32;
+                    aux_data_end =
+                        (uint64)(uint32)global->init_expr.u.unary.v.i32;
                     aux_data_end_global_index = export->index;
                     LOG_VERBOSE("Found aux __data_end global, value: %" PRIu64,
                                 aux_data_end);
@@ -2838,10 +3013,11 @@ load_from_sections(WASMModule *module, WASMSection *sections,
                         && global->type.val_type == VALUE_TYPE_I32
                         && global->init_expr.init_expr_type
                                == INIT_EXPR_TYPE_I32_CONST
-                        && (uint64)(uint32)global->init_expr.u.i32
+                        && (uint64)(uint32)global->init_expr.u.unary.v.i32
                                <= aux_heap_base) {
                         aux_stack_top_global = global;
-                        aux_stack_top = (uint64)(uint32)global->init_expr.u.i32;
+                        aux_stack_top =
+                            (uint64)(uint32)global->init_expr.u.unary.v.i32;
                         module->aux_stack_top_global_index =
                             module->import_global_count + global_index;
                         module->aux_stack_bottom = aux_stack_top;
@@ -3448,8 +3624,14 @@ wasm_loader_unload(WASMModule *module)
     if (module->memories)
         wasm_runtime_free(module->memories);
 
-    if (module->globals)
+    if (module->globals) {
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+        for (i = 0; i < module->global_count; i++) {
+            destroy_init_expr(&module->globals[i].init_expr);
+        }
+#endif
         wasm_runtime_free(module->globals);
+    }
 
     if (module->exports)
         wasm_runtime_free(module->exports);
@@ -3458,6 +3640,9 @@ wasm_loader_unload(WASMModule *module)
         for (i = 0; i < module->table_seg_count; i++) {
             if (module->table_segments[i].init_values)
                 wasm_runtime_free(module->table_segments[i].init_values);
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            destroy_init_expr(&module->table_segments[i].base_offset);
+#endif
         }
         wasm_runtime_free(module->table_segments);
     }
@@ -3467,6 +3652,9 @@ wasm_loader_unload(WASMModule *module)
             if (module->data_segments[i]) {
                 if (module->data_segments[i]->is_data_cloned)
                     wasm_runtime_free(module->data_segments[i]->data);
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+                destroy_init_expr(&module->data_segments[i]->base_offset);
+#endif
                 wasm_runtime_free(module->data_segments[i]);
             }
         }
@@ -7320,7 +7508,8 @@ re_scan:
                                 == VALUE_TYPE_FUNCREF
                             && module->globals[i].init_expr.init_expr_type
                                    == INIT_EXPR_TYPE_FUNCREF_CONST
-                            && module->globals[i].init_expr.u.u32 == func_idx) {
+                            && module->globals[i].init_expr.u.unary.v.ref_index
+                                   == func_idx) {
                             func_declared = true;
                             break;
                         }
@@ -7334,7 +7523,8 @@ re_scan:
                              i++, table_seg++) {
                             if (table_seg->elem_type == VALUE_TYPE_FUNCREF) {
                                 for (j = 0; j < table_seg->value_count; j++) {
-                                    if (table_seg->init_values[j].u.ref_index
+                                    if (table_seg->init_values[j]
+                                            .u.unary.v.ref_index
                                         == func_idx) {
                                         func_declared = true;
                                         break;

+ 171 - 110
core/iwasm/interpreter/wasm_runtime.c

@@ -1165,6 +1165,81 @@ instantiate_array_global_recursive(WASMModule *module,
 }
 #endif
 
+static bool
+get_init_value_recursive(WASMModule *module, InitializerExpression *expr,
+                         WASMGlobalInstance *globals, WASMValue *value,
+                         char *error_buf, uint32 error_buf_size)
+{
+    uint8 flag = expr->init_expr_type;
+    switch (flag) {
+        case INIT_EXPR_TYPE_GET_GLOBAL:
+        {
+            if (!check_global_init_expr(module, expr->u.unary.v.global_index,
+                                        error_buf, error_buf_size)) {
+                goto fail;
+            }
+
+            *value = globals[expr->u.unary.v.global_index].initial_value;
+            break;
+        }
+        case INIT_EXPR_TYPE_I32_CONST:
+        case INIT_EXPR_TYPE_I64_CONST:
+        {
+            *value = expr->u.unary.v;
+            break;
+        }
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+        case INIT_EXPR_TYPE_I32_ADD:
+        case INIT_EXPR_TYPE_I32_SUB:
+        case INIT_EXPR_TYPE_I32_MUL:
+        case INIT_EXPR_TYPE_I64_ADD:
+        case INIT_EXPR_TYPE_I64_SUB:
+        case INIT_EXPR_TYPE_I64_MUL:
+        {
+            WASMValue l_value, r_value;
+            if (!expr->u.binary.l_expr || !expr->u.binary.r_expr) {
+                goto fail;
+            }
+            if (!get_init_value_recursive(module, expr->u.binary.l_expr,
+                                          globals, &l_value, error_buf,
+                                          error_buf_size)) {
+                goto fail;
+            }
+            if (!get_init_value_recursive(module, expr->u.binary.r_expr,
+                                          globals, &r_value, error_buf,
+                                          error_buf_size)) {
+                goto fail;
+            }
+
+            if (flag == INIT_EXPR_TYPE_I32_ADD) {
+                value->i32 = l_value.i32 + r_value.i32;
+            }
+            else if (flag == INIT_EXPR_TYPE_I32_SUB) {
+                value->i32 = l_value.i32 - r_value.i32;
+            }
+            else if (flag == INIT_EXPR_TYPE_I32_MUL) {
+                value->i32 = l_value.i32 * r_value.i32;
+            }
+            else if (flag == INIT_EXPR_TYPE_I64_ADD) {
+                value->i64 = l_value.i64 + r_value.i64;
+            }
+            else if (flag == INIT_EXPR_TYPE_I64_SUB) {
+                value->i64 = l_value.i64 - r_value.i64;
+            }
+            else if (flag == INIT_EXPR_TYPE_I64_MUL) {
+                value->i64 = l_value.i64 * r_value.i64;
+            }
+            break;
+        }
+#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */
+        default:
+            goto fail;
+    }
+    return true;
+fail:
+    return false;
+}
+
 /**
  * Instantiate globals in a module.
  */
@@ -1209,7 +1284,7 @@ globals_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
             /* The linked global instance has been initialized, we
                just need to copy the value. */
             global->initial_value =
-                global_import->import_global_linked->init_expr.u;
+                global_import->import_global_linked->init_expr.u.unary.v;
         }
         else
 #endif
@@ -1245,17 +1320,23 @@ globals_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
 #endif
 
         switch (flag) {
+            case INIT_EXPR_TYPE_I32_CONST:
+            case INIT_EXPR_TYPE_I64_CONST:
             case INIT_EXPR_TYPE_GET_GLOBAL:
+#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0
+            case INIT_EXPR_TYPE_I32_ADD:
+            case INIT_EXPR_TYPE_I32_SUB:
+            case INIT_EXPR_TYPE_I32_MUL:
+            case INIT_EXPR_TYPE_I64_ADD:
+            case INIT_EXPR_TYPE_I64_SUB:
+            case INIT_EXPR_TYPE_I64_MUL:
+#endif
             {
-                if (!check_global_init_expr(module, init_expr->u.global_index,
-                                            error_buf, error_buf_size)) {
+                if (!get_init_value_recursive(module, init_expr, globals,
+                                              &global->initial_value, error_buf,
+                                              error_buf_size)) {
                     goto fail;
                 }
-
-                bh_memcpy_s(
-                    &(global->initial_value), sizeof(WASMValue),
-                    &(globals[init_expr->u.global_index].initial_value),
-                    sizeof(globals[init_expr->u.global_index].initial_value));
                 break;
             }
 #if WASM_ENABLE_GC != 0
@@ -1267,11 +1348,12 @@ globals_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
                 uint32 type_idx;
 
                 if (flag == INIT_EXPR_TYPE_STRUCT_NEW) {
-                    init_values = (WASMStructNewInitValues *)init_expr->u.data;
+                    init_values =
+                        (WASMStructNewInitValues *)init_expr->u.unary.v.data;
                     type_idx = init_values->type_idx;
                 }
                 else {
-                    type_idx = init_expr->u.type_index;
+                    type_idx = init_expr->u.unary.v.type_index;
                 }
 
                 struct_obj = instantiate_struct_global_recursive(
@@ -1294,12 +1376,14 @@ globals_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
                 uint32 type_idx, len;
 
                 if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) {
-                    type_idx = init_expr->u.array_new_default.type_index;
-                    len = init_expr->u.array_new_default.length;
+                    type_idx =
+                        init_expr->u.unary.v.array_new_default.type_index;
+                    len = init_expr->u.unary.v.array_new_default.length;
                     array_init_value = &empty_value;
                 }
                 else {
-                    init_values = (WASMArrayNewInitValues *)init_expr->u.data;
+                    init_values =
+                        (WASMArrayNewInitValues *)init_expr->u.unary.v.data;
                     type_idx = init_values->type_idx;
                     len = init_values->length;
 
@@ -1318,13 +1402,12 @@ globals_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
             case INIT_EXPR_TYPE_I31_NEW:
             {
                 global->initial_value.gc_obj =
-                    (wasm_obj_t)wasm_i31_obj_new(init_expr->u.i32);
+                    (wasm_obj_t)wasm_i31_obj_new(init_expr->u.unary.v.i32);
                 break;
             }
 #endif /* end of WASM_ENABLE_GC != 0 */
             default:
-                bh_memcpy_s(&(global->initial_value), sizeof(WASMValue),
-                            &(init_expr->u), sizeof(init_expr->u));
+                global->initial_value = init_expr->u.unary.v;
                 break;
         }
 
@@ -2698,6 +2781,7 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
         uint8 *memory_data = NULL;
         uint64 memory_size = 0;
         WASMDataSeg *data_seg = module->data_segments[i];
+        WASMValue offset_value;
 
 #if WASM_ENABLE_BULK_MEMORY != 0
         if (data_seg->is_passive)
@@ -2717,54 +2801,37 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
             (uint64)memory->num_bytes_per_page * memory->cur_page_count;
         bh_assert(memory_data || memory_size == 0);
 
-        bh_assert(data_seg->base_offset.init_expr_type
-                      == INIT_EXPR_TYPE_GET_GLOBAL
-                  || data_seg->base_offset.init_expr_type
-                         == (memory->is_memory64 ? INIT_EXPR_TYPE_I64_CONST
-                                                 : INIT_EXPR_TYPE_I32_CONST));
+        uint8 offset_flag = data_seg->base_offset.init_expr_type;
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || (memory->is_memory64 ? is_valid_i64_offset(offset_flag)
+                                          : is_valid_i32_offset(offset_flag)));
 
-        if (data_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
-            if (!check_global_init_expr(module,
-                                        data_seg->base_offset.u.global_index,
-                                        error_buf, error_buf_size)) {
-                goto fail;
-            }
+        if (!get_init_value_recursive(module, &data_seg->base_offset, globals,
+                                      &offset_value, error_buf,
+                                      error_buf_size)) {
+            goto fail;
+        }
 
+        if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) {
             if (!globals
-                || globals[data_seg->base_offset.u.global_index].type
+                || globals[data_seg->base_offset.u.unary.v.global_index].type
                        != (memory->is_memory64 ? VALUE_TYPE_I64
                                                : VALUE_TYPE_I32)) {
                 set_error_buf(error_buf, error_buf_size,
                               "data segment does not fit");
                 goto fail;
             }
+        }
 
 #if WASM_ENABLE_MEMORY64 != 0
-            if (memory->is_memory64) {
-                base_offset =
-                    (uint64)globals[data_seg->base_offset.u.global_index]
-                        .initial_value.i64;
-            }
-            else
-#endif
-            {
-                base_offset =
-                    (uint32)globals[data_seg->base_offset.u.global_index]
-                        .initial_value.i32;
-            }
+        if (memory->is_memory64) {
+            base_offset = (uint64)offset_value.i64;
         }
-        else {
-#if WASM_ENABLE_MEMORY64 != 0
-            if (memory->is_memory64) {
-                base_offset = (uint64)data_seg->base_offset.u.i64;
-            }
-            else
+        else
 #endif
-            {
-                base_offset = (uint32)data_seg->base_offset.u.i32;
-            }
+        {
+            base_offset = (uint32)offset_value.i32;
         }
-
         /* check offset */
         if (base_offset > memory_size) {
 #if WASM_ENABLE_MEMORY64 != 0
@@ -2842,36 +2909,39 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
             || table->init_expr.init_expr_type == INIT_EXPR_TYPE_REFNULL_CONST);
 
         if (table->init_expr.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) {
-            if (!check_global_init_expr(module, table->init_expr.u.global_index,
+            if (!check_global_init_expr(module,
+                                        table->init_expr.u.unary.v.global_index,
                                         error_buf, error_buf_size)) {
                 goto fail;
             }
 
-            table->init_expr.u.gc_obj =
-                globals[table->init_expr.u.global_index].initial_value.gc_obj;
+            table->init_expr.u.unary.v.gc_obj =
+                globals[table->init_expr.u.unary.v.global_index]
+                    .initial_value.gc_obj;
         }
         else if (table->init_expr.init_expr_type
                  == INIT_EXPR_TYPE_FUNCREF_CONST) {
-            uint32 func_idx = table->init_expr.u.ref_index;
+            uint32 func_idx = table->init_expr.u.unary.v.ref_index;
             if (func_idx != UINT32_MAX) {
-                if (!(table->init_expr.u.gc_obj =
+                if (!(table->init_expr.u.unary.v.gc_obj =
                           wasm_create_func_obj(module_inst, func_idx, false,
                                                error_buf, error_buf_size)))
                     goto fail;
             }
             else {
-                table->init_expr.u.gc_obj = NULL_REF;
+                table->init_expr.u.unary.v.gc_obj = NULL_REF;
             }
         }
         else if (table->init_expr.init_expr_type
                  == INIT_EXPR_TYPE_REFNULL_CONST) {
-            table->init_expr.u.gc_obj = NULL_REF;
+            table->init_expr.u.unary.v.gc_obj = NULL_REF;
         }
 
         LOG_DEBUG("Init table [%d] elements from [%d] to [%d] as: %p", i, 0,
-                  table_inst->cur_size, (void *)table->init_expr.u.gc_obj);
+                  table_inst->cur_size,
+                  (void *)table->init_expr.u.unary.v.gc_obj);
         for (j = 0; j < table_inst->cur_size; j++) {
-            *(table_data + j) = table->init_expr.u.gc_obj;
+            *(table_data + j) = table->init_expr.u.unary.v.gc_obj;
         }
     }
 #endif /* end of WASM_ENABLE_GC != 0 */
@@ -2883,6 +2953,7 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
         /* has check it in loader */
         WASMTableInstance *table = module_inst->tables[table_seg->table_index];
         table_elem_type_t *table_data;
+        WASMValue offset_value;
         uint32 j;
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
         uint8 tbl_elem_type;
@@ -2941,48 +3012,37 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
             continue;
 #endif
 
+        uint8 offset_flag = table_seg->base_offset.init_expr_type;
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
-        bh_assert(table_seg->base_offset.init_expr_type
-                      == INIT_EXPR_TYPE_I32_CONST
-                  || table_seg->base_offset.init_expr_type
-                         == INIT_EXPR_TYPE_GET_GLOBAL
-                  || table_seg->base_offset.init_expr_type
-                         == INIT_EXPR_TYPE_FUNCREF_CONST
-                  || table_seg->base_offset.init_expr_type
-                         == INIT_EXPR_TYPE_REFNULL_CONST);
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || offset_flag == INIT_EXPR_TYPE_FUNCREF_CONST
+                  || offset_flag == INIT_EXPR_TYPE_REFNULL_CONST
+                  || is_valid_i32_offset(offset_flag));
 #else
-        bh_assert(table_seg->base_offset.init_expr_type
-                      == INIT_EXPR_TYPE_I32_CONST
-                  || table_seg->base_offset.init_expr_type
-                         == INIT_EXPR_TYPE_GET_GLOBAL);
+        bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL
+                  || is_valid_i32_offset(offset_flag));
 #endif
 
-        /* init vec(funcidx) or vec(expr) */
-        if (table_seg->base_offset.init_expr_type
-            == INIT_EXPR_TYPE_GET_GLOBAL) {
-            if (!check_global_init_expr(module,
-                                        table_seg->base_offset.u.global_index,
-                                        error_buf, error_buf_size)) {
-                goto fail;
-            }
+        if (!get_init_value_recursive(module, &table_seg->base_offset, globals,
+                                      &offset_value, error_buf,
+                                      error_buf_size)) {
+            goto fail;
+        }
 
+        if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) {
             if (!globals
-                || globals[table_seg->base_offset.u.global_index].type
+                || globals[table_seg->base_offset.u.unary.v.global_index].type
                        != VALUE_TYPE_I32) {
                 set_error_buf(error_buf, error_buf_size,
                               "type mismatch: elements segment does not fit");
                 goto fail;
             }
-
-            table_seg->base_offset.u.i32 =
-                globals[table_seg->base_offset.u.global_index]
-                    .initial_value.i32;
         }
 
         /* check offset since length might negative */
-        if ((uint32)table_seg->base_offset.u.i32 > table->cur_size) {
-            LOG_DEBUG("base_offset(%d) > table->cur_size(%d)",
-                      table_seg->base_offset.u.i32, table->cur_size);
+        if ((uint32)offset_value.i32 > table->cur_size) {
+            LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", offset_value.i32,
+                      table->cur_size);
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
             set_error_buf(error_buf, error_buf_size,
                           "out of bounds table access");
@@ -2995,9 +3055,9 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
 
         /* check offset + length(could be zero) */
         length = table_seg->value_count;
-        if ((uint32)table_seg->base_offset.u.i32 + length > table->cur_size) {
+        if ((uint32)offset_value.i32 + length > table->cur_size) {
             LOG_DEBUG("base_offset(%d) + length(%d)> table->cur_size(%d)",
-                      table_seg->base_offset.u.i32, length, table->cur_size);
+                      offset_value.i32, length, table->cur_size);
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
             set_error_buf(error_buf, error_buf_size,
                           "out of bounds table access");
@@ -3027,10 +3087,10 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
                 case INIT_EXPR_TYPE_FUNCREF_CONST:
                 {
 #if WASM_ENABLE_GC == 0
-                    ref = (void *)(uintptr_t)init_expr->u.ref_index;
+                    ref = (void *)(uintptr_t)init_expr->u.unary.v.ref_index;
 #else
                     WASMFuncObjectRef func_obj;
-                    uint32 func_idx = init_expr->u.ref_index;
+                    uint32 func_idx = init_expr->u.unary.v.ref_index;
                     /* UINT32_MAX indicates that it is a null reference */
                     if (func_idx != UINT32_MAX) {
                         if (!(func_obj = wasm_create_func_obj(
@@ -3049,14 +3109,14 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
 #if WASM_ENABLE_GC != 0
                 case INIT_EXPR_TYPE_GET_GLOBAL:
                 {
-                    if (!check_global_init_expr(module,
-                                                init_expr->u.global_index,
-                                                error_buf, error_buf_size)) {
+                    if (!check_global_init_expr(
+                            module, init_expr->u.unary.v.global_index,
+                            error_buf, error_buf_size)) {
                         goto fail;
                     }
 
-                    ref =
-                        globals[init_expr->u.global_index].initial_value.gc_obj;
+                    ref = globals[init_expr->u.unary.v.global_index]
+                              .initial_value.gc_obj;
                     break;
                 }
                 case INIT_EXPR_TYPE_STRUCT_NEW:
@@ -3069,12 +3129,12 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
                     uint32 type_idx;
 
                     if (flag == INIT_EXPR_TYPE_STRUCT_NEW) {
-                        init_values =
-                            (WASMStructNewInitValues *)init_expr->u.data;
+                        init_values = (WASMStructNewInitValues *)
+                                          init_expr->u.unary.v.data;
                         type_idx = init_values->type_idx;
                     }
                     else {
-                        type_idx = init_expr->u.type_index;
+                        type_idx = init_expr->u.unary.v.type_index;
                     }
 
                     struct_type = (WASMStructType *)module->types[type_idx];
@@ -3125,13 +3185,14 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
                     uint32 type_idx, len;
 
                     if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) {
-                        type_idx = init_expr->u.array_new_default.type_index;
-                        len = init_expr->u.array_new_default.length;
+                        type_idx =
+                            init_expr->u.unary.v.array_new_default.type_index;
+                        len = init_expr->u.unary.v.array_new_default.length;
                         arr_init_val = &empty_val;
                     }
                     else {
                         init_values =
-                            (WASMArrayNewInitValues *)init_expr->u.data;
+                            (WASMArrayNewInitValues *)init_expr->u.unary.v.data;
                         type_idx = init_values->type_idx;
                         len = init_values->length;
 
@@ -3177,14 +3238,14 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
                 }
                 case INIT_EXPR_TYPE_I31_NEW:
                 {
-                    ref = (wasm_obj_t)wasm_i31_obj_new(init_expr->u.i32);
+                    ref =
+                        (wasm_obj_t)wasm_i31_obj_new(init_expr->u.unary.v.i32);
                     break;
                 }
 #endif /* end of WASM_ENABLE_GC != 0 */
             }
 
-            *(table_data + table_seg->base_offset.u.i32 + j) =
-                (table_elem_type_t)ref;
+            *(table_data + offset_value.i32 + j) = (table_elem_type_t)ref;
         }
     }
 
@@ -4706,10 +4767,10 @@ llvm_jit_table_init(WASMModuleInstance *module_inst, uint32 tbl_idx,
     for (i = 0; i < length; i++) {
 #if WASM_ENABLE_GC != 0
         /* UINT32_MAX indicates that it is a null ref */
-        if (init_values[i].u.ref_index != UINT32_MAX) {
-            if (!(func_obj = wasm_create_func_obj(module_inst,
-                                                  init_values[i].u.ref_index,
-                                                  true, NULL, 0))) {
+        if (init_values[i].u.unary.v.ref_index != UINT32_MAX) {
+            if (!(func_obj = wasm_create_func_obj(
+                      module_inst, init_values[i].u.unary.v.ref_index, true,
+                      NULL, 0))) {
                 wasm_set_exception(module_inst, "null function reference");
                 return;
             }
@@ -4719,7 +4780,7 @@ llvm_jit_table_init(WASMModuleInstance *module_inst, uint32 tbl_idx,
             table_elems[i] = NULL_REF;
         }
 #else
-        table_elems[i] = init_values[i].u.ref_index;
+        table_elems[i] = init_values[i].u.unary.v.ref_index;
 #endif
     }
 }

+ 4 - 0
doc/build_wamr.md

@@ -293,6 +293,10 @@ Currently we only profile the memory consumption of module, module_instance and
 - **WAMR_BUILD_AOT_INTRINSICS**=1/0, enable the AOT intrinsic functions, default to enable if not set. These functions can be called from the AOT code when `--disable-llvm-intrinsics` flag or `--enable-builtin-intrinsics=<intr1,intr2,...>` flag is used by wamrc to generate the AOT file.
 > Note: See [Tuning the XIP intrinsic functions](./xip.md#tuning-the-xip-intrinsic-functions) for more details.
 
+### **Enable extended constant expression**
+- **WAMR_BUILD_EXTENDED_CONST_EXPR**=1/0, default to disable if not set.
+> Note: See [Extended Constant Expressions](https://github.com/WebAssembly/extended-const/blob/main/proposals/extended-const/Overview.md) for more details.
+
 ### **Configurable memory access boundary check**
 - **WAMR_CONFIGURABLE_BOUNDS_CHECKS**=1/0, default to disable if not set
 > Note: If it is enabled, allow to run `iwasm --disable-bounds-checks` to disable the memory access boundary checks for interpreter mode.

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

@@ -172,6 +172,7 @@ def test_case(
     clean_up_flag=True,
     verbose_flag=True,
     gc_flag=False,
+    extended_const_flag=False,
     memory64_flag=False,
     multi_memory_flag=False,
     qemu_flag=False,
@@ -229,6 +230,9 @@ def test_case(
     if gc_flag:
         CMD.append("--gc")
 
+    if extended_const_flag:
+        CMD.append("--extended-const")
+
     if memory64_flag:
         CMD.append("--memory64")
 
@@ -304,6 +308,7 @@ def test_suite(
     clean_up_flag=True,
     verbose_flag=True,
     gc_flag=False,
+    extended_const_flag=False,
     memory64_flag=False,
     multi_memory_flag=False,
     parl_flag=False,
@@ -385,6 +390,7 @@ def test_suite(
                         clean_up_flag,
                         verbose_flag,
                         gc_flag,
+                        extended_const_flag,
                         memory64_flag,
                         multi_memory_flag,
                         qemu_flag,
@@ -428,6 +434,7 @@ def test_suite(
                     clean_up_flag,
                     verbose_flag,
                     gc_flag,
+                    extended_const_flag,
                     memory64_flag,
                     multi_memory_flag,
                     qemu_flag,
@@ -561,6 +568,13 @@ def main():
         dest="gc_flag",
         help="Running with GC feature",
     )
+    parser.add_argument(
+        "--enable-extended-const",
+        action="store_true",
+        default=False,
+        dest="extended_const_flag",
+        help="Running with extended const expression feature",
+    )
     parser.add_argument(
         "--memory64",
         action="store_true",
@@ -619,6 +633,7 @@ def main():
             options.clean_up_flag,
             options.verbose_flag,
             options.gc_flag,
+            options.extended_const_flag,
             options.memory64_flag,
             options.multi_memory_flag,
             options.parl_flag,
@@ -648,6 +663,7 @@ def main():
                     options.clean_up_flag,
                     options.verbose_flag,
                     options.gc_flag,
+                    options.extended_const_flag,
                     options.memory64_flag,
                     options.multi_memory_flag,
                     options.qemu_flag,

+ 506 - 0
tests/wamr-test-suites/spec-test-script/extended_const.patch

@@ -0,0 +1,506 @@
+diff --git a/test/core/elem.wast b/test/core/elem.wast
+index 92dab52..3954bca 100644
+--- a/test/core/elem.wast
++++ b/test/core/elem.wast
+@@ -571,6 +571,7 @@
+ 
+ ;; Element sections across multiple modules change the same table
+ 
++(;
+ (module $module1
+   (type $out-i32 (func (result i32)))
+   (table (export "shared-table") 10 funcref)
+@@ -620,7 +621,7 @@
+ (assert_return (invoke $module1 "call-7") (i32.const 67))
+ (assert_return (invoke $module1 "call-8") (i32.const 69))
+ (assert_return (invoke $module1 "call-9") (i32.const 70))
+-
++;)
+ ;; Element segments must match element type of table
+ 
+ (assert_invalid
+@@ -659,24 +660,30 @@
+ 	(func (export "set") (param $i i32) (param $x externref)
+ 	      (table.set $t (local.get $i) (local.get $x))))
+ 
+-(register "exporter" $m)
++;; (register "exporter" $m)
+ 
+-(assert_return (invoke $m "get" (i32.const 0)) (ref.null extern))
+-(assert_return (invoke $m "get" (i32.const 1)) (ref.null extern))
++;; (assert_return (invoke $m "get" (i32.const 0)) (ref.null extern))
++;; (assert_return (invoke $m "get" (i32.const 1)) (ref.null extern))
++(assert_return (invoke "get" (i32.const 0)) (ref.null extern))
++(assert_return (invoke "get" (i32.const 1)) (ref.null extern))
+ 
+-(assert_return (invoke $m "set" (i32.const 0) (ref.extern 42)))
+-(assert_return (invoke $m "set" (i32.const 1) (ref.extern 137)))
+-
+-(assert_return (invoke $m "get" (i32.const 0)) (ref.extern 42))
+-(assert_return (invoke $m "get" (i32.const 1)) (ref.extern 137))
++;; (assert_return (invoke $m "set" (i32.const 0) (ref.extern 42)))
++;; (assert_return (invoke $m "set" (i32.const 1) (ref.extern 137)))
++(assert_return (invoke "set" (i32.const 0) (ref.extern 42)))
++(assert_return (invoke "set" (i32.const 1) (ref.extern 137)))
+ 
++;; (assert_return (invoke $m "get" (i32.const 0)) (ref.extern 42))
++;; (assert_return (invoke $m "get" (i32.const 1)) (ref.extern 137))
++(assert_return (invoke "get" (i32.const 0)) (ref.extern 42))
++(assert_return (invoke "get" (i32.const 1)) (ref.extern 137))
++(;
+ (module
+   (import "exporter" "table" (table $t 2 externref))
+   (elem (i32.const 0) externref (ref.null extern)))
+ 
+ (assert_return (invoke $m "get" (i32.const 0)) (ref.null extern))
+ (assert_return (invoke $m "get" (i32.const 1)) (ref.extern 137))
+-
++;)
+ ;; Initializing a table with imported funcref global
+ 
+ (module $module4
+@@ -686,6 +693,7 @@
+   (global (export "f") funcref (ref.func 0))
+ )
+ 
++(;
+ (register "module4" $module4)
+ 
+ (module
+@@ -699,6 +707,7 @@
+ )
+ 
+ (assert_return (invoke "call_imported_elem") (i32.const 42))
++;)
+ 
+ ;; Extended contant expressions
+ 
+diff --git a/test/core/ref_func.wast b/test/core/ref_func.wast
+index adb5cb7..6396013 100644
+--- a/test/core/ref_func.wast
++++ b/test/core/ref_func.wast
+@@ -4,7 +4,7 @@
+ (register "M")
+ 
+ (module
+-  (func $f (import "M" "f") (param i32) (result i32))
++  (func $f (param $x i32) (result i32) (local.get $x))
+   (func $g (param $x i32) (result i32)
+     (i32.add (local.get $x) (i32.const 1))
+   )
+diff --git a/test/core/table_copy.wast b/test/core/table_copy.wast
+index 380e84e..59230cf 100644
+--- a/test/core/table_copy.wast
++++ b/test/core/table_copy.wast
+@@ -14,11 +14,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -106,11 +106,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -198,11 +198,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -290,11 +290,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -382,11 +382,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -474,11 +474,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -566,11 +566,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -658,11 +658,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -750,11 +750,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -842,11 +842,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -934,11 +934,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1026,11 +1026,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1118,11 +1118,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1210,11 +1210,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1302,11 +1302,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1394,11 +1394,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1486,11 +1486,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -1578,11 +1578,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+diff --git a/test/core/table_init.wast b/test/core/table_init.wast
+index 0b2d26f..3c595e5 100644
+--- a/test/core/table_init.wast
++++ b/test/core/table_init.wast
+@@ -14,11 +14,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -72,11 +72,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -130,11 +130,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t0) (i32.const 2) func 3 1 4 1)
+@@ -196,11 +196,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -254,11 +254,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)
+@@ -312,11 +312,11 @@
+ 
+ (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
++  (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)
+   (elem (table $t1) (i32.const 2) func 3 1 4 1)

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

@@ -336,6 +336,9 @@ parser.add_argument('--multi-thread', default=False, action='store_true',
 parser.add_argument('--gc', default=False, action='store_true',
         help='Test with GC')
 
+parser.add_argument('--extended-const', action='store_true',
+                       help='Enable extended const expression feature')
+
 parser.add_argument('--memory64', default=False, action='store_true',
         help='Test with Memory64')
 
@@ -1112,6 +1115,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
         cmd = [opts.wast2wasm, "--enable-memory64", "--no-check", wast_tempfile, "-o", wasm_tempfile ]
     elif opts.multi_memory:
         cmd = [opts.wast2wasm, "--enable-multi-memory", "--no-check", wast_tempfile, "-o", wasm_tempfile ]
+    elif opts.extended_const:
+        cmd = [opts.wast2wasm, "--enable-extended-const", "--no-check", wast_tempfile, "-o", wasm_tempfile ]
     else:
         # `--enable-multi-memory` for a case in memory.wast but doesn't require runtime support
         cmd = [opts.wast2wasm, "--enable-multi-memory", "--enable-threads", "--no-check",
@@ -1155,6 +1160,9 @@ def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r, output = '
         cmd.append("--enable-gc")
         cmd.append("--enable-tail-call")
 
+    if opts.extended_const:
+        cmd.append("--enable-extended-const")
+
     if output == 'object':
         cmd.append("--format=object")
     elif output == 'ir':

+ 27 - 1
tests/wamr-test-suites/test_wamr.sh

@@ -41,6 +41,7 @@ function help()
     echo "-j set the platform to test"
     echo "-T set sanitizer to use in tests(ubsan|tsan|asan|posan)"
     echo "-A use the specified wamrc command instead of building it"
+    echo "-N enable extended const expression feature"
     echo "-r [requirement name] [N [N ...]] specify a requirement name followed by one or more"
     echo "                                  subrequirement IDs, if no subrequirement is specificed,"
     echo "                                  it will run all subrequirements. When this optin is used,"
@@ -59,6 +60,7 @@ ENABLE_MULTI_THREAD=0
 COLLECT_CODE_COVERAGE=0
 ENABLE_SIMD=0
 ENABLE_GC=0
+ENABLE_EXTENDED_CONST_EXPR=0
 ENABLE_MEMORY64=0
 ENABLE_MULTI_MEMORY=0
 ENABLE_XIP=0
@@ -87,7 +89,7 @@ REQUIREMENT_NAME=""
 # Initialize an empty array for subrequirement IDs
 SUBREQUIREMENT_IDS=()
 
-while getopts ":s:cabgvt:m:MCpSXexwWEPGQF:j:T:r:A:" opt
+while getopts ":s:cabgvt:m:MCpSXexwWEPGQF:j:T:r:A:N" opt
 do
     OPT_PARSED="TRUE"
     case $opt in
@@ -191,6 +193,10 @@ do
         echo "enable GC feature"
         ENABLE_GC=1
         ;;
+        N)
+        echo "enable extended const expression feature"
+        ENABLE_EXTENDED_CONST_EXPR=1
+        ;;
         P)
         PARALLELISM=1
         ;;
@@ -485,6 +491,17 @@ function spec_test()
         #     (func $f (param (ref null $t)) (result funcref) (local.get 0))
         #
         compile_reference_interpreter
+    elif [[ ${ENABLE_EXTENDED_CONST_EXPR} == 1 ]]; then
+        echo "checkout spec for extended const expression proposal"
+
+        git clone -b main --single-branch https://github.com/WebAssembly/extended-const.git spec
+        pushd spec
+
+        # Jan 14, 2025. README.md: Add note that this proposal is done (#20)
+        git reset --hard 8d4f6aa2b00a8e7c0174410028625c6a176db8a1
+        # ignore import table cases
+        git apply --ignore-whitespace ../../spec-test-script/extended_const.patch || exit 1
+        
     elif [[ ${ENABLE_MEMORY64} == 1 ]]; then
         echo "checkout spec for memory64 proposal"
 
@@ -587,6 +604,10 @@ function spec_test()
         ARGS_FOR_SPEC_TEST+="--gc "
     fi
 
+    if [[ ${ENABLE_EXTENDED_CONST_EXPR} == 1 ]]; then
+        ARGS_FOR_SPEC_TEST+="--enable-extended-const "
+    fi
+
     if [[ 1 == ${ENABLE_MEMORY64} ]]; then
         ARGS_FOR_SPEC_TEST+="--memory64 "
     fi
@@ -832,6 +853,7 @@ function build_wamrc()
         && cmake .. \
              -DCOLLECT_CODE_COVERAGE=${COLLECT_CODE_COVERAGE} \
              -DWAMR_BUILD_SHRUNK_MEMORY=0 \
+             -DWAMR_BUILD_EXTENDED_CONST_EXPR=${ENABLE_EXTENDED_CONST_EXPR} \
         && make -j 4
 }
 
@@ -1023,6 +1045,10 @@ function trigger()
         EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_TAIL_CALL=1"
     fi
 
+    if [[ ${ENABLE_EXTENDED_CONST_EXPR} == 1 ]]; then
+        EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_EXTENDED_CONST_EXPR=1"
+    fi
+
     if [[ ${ENABLE_DEBUG_VERSION} == 1 ]]; then
         EXTRA_COMPILE_FLAGS+=" -DCMAKE_BUILD_TYPE=Debug"
     fi

+ 1 - 0
wamr-compiler/CMakeLists.txt

@@ -53,6 +53,7 @@ add_definitions(-DWASM_ENABLE_PERF_PROFILING=1)
 add_definitions(-DWASM_ENABLE_LOAD_CUSTOM_SECTION=1)
 add_definitions(-DWASM_ENABLE_MODULE_INST_CONTEXT=1)
 add_definitions(-DWASM_ENABLE_MEMORY64=1)
+add_definitions(-DWASM_ENABLE_EXTENDED_CONST_EXPR=1)
 
 add_definitions(-DWASM_ENABLE_GC=1)
 

+ 4 - 0
wamr-compiler/main.c

@@ -423,6 +423,7 @@ main(int argc, char *argv[])
     option.enable_bulk_memory = true;
     option.enable_ref_types = true;
     option.enable_gc = false;
+    option.enable_extended_const = false;
     aot_call_stack_features_init_default(&option.call_stack_features);
 
     /* Process options */
@@ -536,6 +537,9 @@ main(int argc, char *argv[])
         else if (!strcmp(argv[0], "--disable-aux-stack-check")) {
             option.enable_aux_stack_check = false;
         }
+        else if (!strcmp(argv[0], "--enable-extended-const")) {
+            option.enable_extended_const = true;
+        }
         else if (!strcmp(argv[0], "--enable-dump-call-stack")) {
             option.aux_stack_frame_type = AOT_STACK_FRAME_TYPE_STANDARD;
         }