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

Merge branch 'main' into dev/merge_aot_data_text

Wenyong Huang 1 год назад
Родитель
Сommit
a8d539d84d

+ 36 - 8
.github/workflows/compilation_on_android_ubuntu.yml

@@ -68,6 +68,7 @@ env:
   WAMR_COMPILER_TEST_OPTIONS: "-s wamr_compiler -S -b -P"
   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"
 
 jobs:
   build_llvm_libraries_on_ubuntu_2204:
@@ -148,6 +149,7 @@ jobs:
             "-DWAMR_BUILD_TAIL_CALL=1",
             "-DWAMR_DISABLE_HW_BOUND_CHECK=1",
             "-DWAMR_BUILD_MEMORY64=1",
+            "-DWAMR_BUILD_MULTI_MEMORY=1",
           ]
         os: [ubuntu-22.04]
         platform: [android, linux]
@@ -206,11 +208,9 @@ jobs:
             make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1"
           - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1"
-          # Memory64 only on CLASSIC INTERP mode, and only on 64-bit platform
+          # Memory64 only on CLASSIC INTERP and AOT mode, and only on 64-bit platform
           - make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
             platform: android
-          - make_options_run_mode: $AOT_BUILD_OPTIONS
-            make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
@@ -221,6 +221,21 @@ jobs:
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
+          # Multi memory only on CLASSIC INTERP mode, and only on 64-bit platform
+          - make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
+            platform: android
+          - make_options_run_mode: $AOT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
           # Fast-JIT and Multi-Tier-JIT mode don't support android
           - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
             platform: android
@@ -593,6 +608,7 @@ jobs:
             $WASI_TEST_OPTIONS,
             $GC_TEST_OPTIONS,
             $MEMORY64_TEST_OPTIONS,
+            $MULTI_MEMORY_TEST_OPTIONS,
           ]
         wasi_sdk_release:
           [
@@ -640,18 +656,30 @@ jobs:
             test_option: $MEMORY64_TEST_OPTIONS
           - running_mode: "multi-tier-jit"
             test_option: $MEMORY64_TEST_OPTIONS
+          # aot, fast-interp, fast-jit, llvm-jit, multi-tier-jit don't support Multi Memory 
+          - running_mode: "aot"
+            test_option: $MULTI_MEMORY_TEST_OPTIONS
+          - running_mode: "fast-interp"
+            test_option: $MULTI_MEMORY_TEST_OPTIONS
+          - running_mode: "fast-jit"
+            test_option: $MULTI_MEMORY_TEST_OPTIONS
+          - running_mode: "jit"
+            test_option: $MULTI_MEMORY_TEST_OPTIONS
+          - running_mode: "multi-tier-jit"
+            test_option: $MULTI_MEMORY_TEST_OPTIONS
+
     steps:
       - name: checkout
         uses: actions/checkout@v4
 
       - name: Set-up OCaml
         uses: ocaml/setup-ocaml@v3
-        if: matrix.test_option == '$GC_TEST_OPTIONS' || matrix.test_option == '$MEMORY64_TEST_OPTIONS'
+        if: matrix.test_option == '$GC_TEST_OPTIONS'
         with:
           ocaml-compiler: 4.13
 
       - name: Set-up Ocamlbuild
-        if: matrix.test_option == '$GC_TEST_OPTIONS' || matrix.test_option == '$MEMORY64_TEST_OPTIONS'
+        if: matrix.test_option == '$GC_TEST_OPTIONS'
         run: opam install ocamlbuild dune menhir
 
       - name: download and install wasi-sdk
@@ -717,13 +745,13 @@ jobs:
 
       - name: run tests
         timeout-minutes: 30
-        if: matrix.test_option != '$GC_TEST_OPTIONS' && matrix.test_option != '$MEMORY64_TEST_OPTIONS'
+        if: matrix.test_option != '$GC_TEST_OPTIONS'
         run: ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }}
         working-directory: ./tests/wamr-test-suites
 
-      - name: run gc or memory64 tests
+      - name: run gc tests
         timeout-minutes: 20
-        if: matrix.test_option == '$GC_TEST_OPTIONS' || matrix.test_option == '$MEMORY64_TEST_OPTIONS'
+        if: matrix.test_option == '$GC_TEST_OPTIONS'
         run: |
           eval $(opam env)
           ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }}

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

@@ -124,6 +124,7 @@ jobs:
           repository: google/bloaty
           submodules: recursive
           path: bloaty
+          ref: 34f4a66559ad4938c1e629e9b5f54630b2b4d7b0
 
       - name: Build Bloaty
         run: |

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

@@ -132,6 +132,7 @@ jobs:
             "-DWAMR_BUILD_TAIL_CALL=1",
             "-DWAMR_DISABLE_HW_BOUND_CHECK=1",
             "-DWAMR_BUILD_MEMORY64=1",
+            "-DWAMR_BUILD_MULTI_MEMORY=1",
           ]
         os: [ubuntu-20.04]
         platform: [android, linux]
@@ -190,11 +191,9 @@ jobs:
             make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1"
           - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1"
-          # Memory64 only on CLASSIC INTERP mode, and only on 64-bit platform
+          # Memory64 only on CLASSIC INTERP and AOT mode, and only on 64-bit platform
           - make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
             platform: android
-          - make_options_run_mode: $AOT_BUILD_OPTIONS
-            make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
@@ -205,6 +204,21 @@ jobs:
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
+          # Multi memory only on CLASSIC INTERP mode, and only on 64-bit platform
+          - make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
+            platform: android
+          - make_options_run_mode: $AOT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
           # Fast-JIT and Multi-Tier-JIT mode don't support android
           - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
             platform: android
@@ -289,6 +303,7 @@ jobs:
             "-DWAMR_BUILD_TAIL_CALL=1",
             "-DWAMR_DISABLE_HW_BOUND_CHECK=1",
             "-DWAMR_BUILD_MEMORY64=1",
+            "-DWAMR_BUILD_MULTI_MEMORY=1",
           ]
         exclude:
           # incompatible feature and platform
@@ -322,6 +337,11 @@ jobs:
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
           - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
             make_options_feature: "-DWAMR_BUILD_MEMORY64=1"
+          # Memory64 only on CLASSIC INTERP mode
+          - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
+          - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS
+            make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1"
     steps:
       - name: Install dependencies
         run: |

+ 5 - 0
build-scripts/config_common.cmake

@@ -265,6 +265,11 @@ if (WAMR_BUILD_MEMORY64 EQUAL 1)
   set (WAMR_DISABLE_HW_BOUND_CHECK 1)
   message ("     Memory64 memory enabled")
 endif ()
+if (WAMR_BUILD_MULTI_MEMORY EQUAL 1)
+  add_definitions (-DWASM_ENABLE_MULTI_MEMORY=1)
+  message ("     Multi memory enabled")
+  set (WAMR_BUILD_DEBUG_INTERP 0)
+endif ()
 if (WAMR_BUILD_THREAD_MGR EQUAL 1)
   message ("     Thread manager enabled")
 endif ()

+ 5 - 0
core/config.h

@@ -664,6 +664,11 @@
 #define WASM_ENABLE_MEMORY64 0
 #endif
 
+/* Disable multi-memory by default */
+#ifndef WASM_ENABLE_MULTI_MEMORY
+#define WASM_ENABLE_MULTI_MEMORY 0
+#endif
+
 #ifndef WASM_TABLE_MAX_SIZE
 #define WASM_TABLE_MAX_SIZE 1024
 #endif

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

@@ -579,6 +579,10 @@ load_target_info_section(const uint8 *buf, const uint8 *buf_end,
         return false;
     }
 
+    /* for backwards compatibility with previous wamrc aot files */
+    if (!strcmp(target_info.arch, "arm64"))
+        bh_strcpy_s(target_info.arch, sizeof(target_info.arch), "aarch64v8");
+
     /* Check machine info */
     if (!check_machine_info(&target_info, error_buf, error_buf_size)) {
         return false;

+ 7 - 0
core/iwasm/aot/aot_runtime.c

@@ -2817,6 +2817,13 @@ aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count)
     return wasm_enlarge_memory(module_inst, inc_page_count);
 }
 
+bool
+aot_enlarge_memory_with_idx(AOTModuleInstance *module_inst,
+                            uint32 inc_page_count, uint32 memidx)
+{
+    return wasm_enlarge_memory_with_idx(module_inst, inc_page_count, memidx);
+}
+
 bool
 aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc,
                   uint32 *argv)

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

@@ -39,6 +39,10 @@ typedef enum AOTSectionType {
     AOT_SECTION_TYPE_FUNCTION = 3,
     AOT_SECTION_TYPE_EXPORT = 4,
     AOT_SECTION_TYPE_RELOCATION = 5,
+    /*
+     * Note: We haven't had anything to use AOT_SECTION_TYPE_SIGNATURE.
+     * It's just reserved for possible module signing features.
+     */
     AOT_SECTION_TYPE_SIGNATURE = 6,
     AOT_SECTION_TYPE_CUSTOM = 100,
 } AOTSectionType;
@@ -612,6 +616,10 @@ aot_module_dup_data(AOTModuleInstance *module_inst, const char *src,
 bool
 aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count);
 
+bool
+aot_enlarge_memory_with_idx(AOTModuleInstance *module_inst,
+                            uint32 inc_page_count, uint32 memidx);
+
 /**
  * Invoke native function from aot code
  */

+ 2 - 9
core/iwasm/aot/arch/aot_reloc_aarch64.c

@@ -53,12 +53,6 @@ get_target_symbol_map(uint32 *sym_num)
     return target_sym_map;
 }
 
-#if (defined(__APPLE__) || defined(__MACH__)) && defined(__arm64__)
-#define BUILD_TARGET_AARCH64_DEFAULT "arm64"
-#else
-#define BUILD_TARGET_AARCH64_DEFAULT "aarch64v8"
-#endif
-
 void
 get_current_target(char *target_buf, uint32 target_buf_size)
 {
@@ -68,8 +62,8 @@ get_current_target(char *target_buf, uint32 target_buf_size)
 
     /* Set to "aarch64v8" by default if sub version isn't specified */
     if (strcmp(s, "AARCH64") == 0) {
-        s = BUILD_TARGET_AARCH64_DEFAULT;
-        s_size = sizeof(BUILD_TARGET_AARCH64_DEFAULT);
+        s = "aarch64v8";
+        s_size = 9; /* strlen("aarch64v8"); */
     }
     if (target_buf_size < s_size) {
         s_size = target_buf_size;
@@ -83,7 +77,6 @@ get_current_target(char *target_buf, uint32 target_buf_size)
     /* Ensure the string is null byte ('\0') terminated */
     *d = '\0';
 }
-#undef BUILD_TARGET_AARCH64_DEFAULT
 
 static uint32
 get_plt_item_size()

+ 36 - 2
core/iwasm/common/wasm_memory.c

@@ -670,6 +670,16 @@ wasm_get_default_memory(WASMModuleInstance *module_inst)
         return NULL;
 }
 
+WASMMemoryInstance *
+wasm_get_memory_with_idx(WASMModuleInstance *module_inst, uint32 index)
+{
+    bh_assert(index < module_inst->memory_count);
+    if (module_inst->memories)
+        return module_inst->memories[index];
+    else
+        return NULL;
+}
+
 void
 wasm_runtime_set_mem_bound_check_bytes(WASMMemoryInstance *memory,
                                        uint64 memory_data_size)
@@ -747,9 +757,14 @@ wasm_mmap_linear_memory(uint64_t map_size, uint64 commit_size)
 }
 
 bool
-wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count)
+wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count,
+                             uint32 memidx)
 {
+#if WASM_ENABLE_MULTI_MEMORY != 0
+    WASMMemoryInstance *memory = wasm_get_memory_with_idx(module, memidx);
+#else
     WASMMemoryInstance *memory = wasm_get_default_memory(module);
+#endif
     uint8 *memory_data_old, *memory_data_new, *heap_data_old;
     uint32 num_bytes_per_page, heap_size;
     uint32 cur_page_count, max_page_count, total_page_count;
@@ -960,7 +975,7 @@ wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count)
     if (module->memory_count > 0)
         shared_memory_lock(module->memories[0]);
 #endif
-    ret = wasm_enlarge_memory_internal(module, inc_page_count);
+    ret = wasm_enlarge_memory_internal(module, inc_page_count, 0);
 #if WASM_ENABLE_SHARED_MEMORY != 0
     if (module->memory_count > 0)
         shared_memory_unlock(module->memories[0]);
@@ -969,6 +984,25 @@ wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count)
     return ret;
 }
 
+bool
+wasm_enlarge_memory_with_idx(WASMModuleInstance *module, uint32 inc_page_count,
+                             uint32 memidx)
+{
+    bool ret = false;
+
+#if WASM_ENABLE_SHARED_MEMORY != 0
+    if (memidx < module->memory_count)
+        shared_memory_lock(module->memories[memidx]);
+#endif
+    ret = wasm_enlarge_memory_internal(module, inc_page_count, memidx);
+#if WASM_ENABLE_SHARED_MEMORY != 0
+    if (memidx < module->memory_count)
+        shared_memory_unlock(module->memories[memidx]);
+#endif
+
+    return ret;
+}
+
 void
 wasm_deallocate_linear_memory(WASMMemoryInstance *memory_inst)
 {

+ 26 - 24
core/iwasm/common/wasm_runtime_common.c

@@ -181,15 +181,36 @@ static RunningMode runtime_running_mode = Mode_Default;
    of signal handler */
 static os_thread_local_attribute WASMExecEnv *exec_env_tls = NULL;
 
+static bool
+is_sig_addr_in_guard_pages(void *sig_addr, WASMModuleInstance *module_inst)
+{
+    WASMMemoryInstance *memory_inst;
+    uint8 *mapped_mem_start_addr = NULL;
+    uint8 *mapped_mem_end_addr = NULL;
+    uint32 i;
+
+    for (i = 0; i < module_inst->memory_count; ++i) {
+        /* To be compatible with multi memory, get the ith memory instance */
+        memory_inst = wasm_get_memory_with_idx(module_inst, i);
+        mapped_mem_start_addr = memory_inst->memory_data;
+        mapped_mem_end_addr = memory_inst->memory_data + 8 * (uint64)BH_GB;
+        if (mapped_mem_start_addr <= (uint8 *)sig_addr
+            && (uint8 *)sig_addr < mapped_mem_end_addr) {
+            /* The address which causes segmentation fault is inside
+               the memory instance's guard regions */
+            return true;
+        }
+    }
+
+    return false;
+}
+
 #ifndef BH_PLATFORM_WINDOWS
 static void
 runtime_signal_handler(void *sig_addr)
 {
     WASMModuleInstance *module_inst;
-    WASMMemoryInstance *memory_inst;
     WASMJmpBuf *jmpbuf_node;
-    uint8 *mapped_mem_start_addr = NULL;
-    uint8 *mapped_mem_end_addr = NULL;
     uint32 page_size = os_getpagesize();
 #if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
     uint8 *stack_min_addr;
@@ -201,23 +222,13 @@ runtime_signal_handler(void *sig_addr)
         && (jmpbuf_node = exec_env_tls->jmpbuf_stack_top)) {
         /* Get mapped mem info of current instance */
         module_inst = (WASMModuleInstance *)exec_env_tls->module_inst;
-        /* Get the default memory instance */
-        memory_inst = wasm_get_default_memory(module_inst);
-        if (memory_inst) {
-            mapped_mem_start_addr = memory_inst->memory_data;
-            mapped_mem_end_addr = memory_inst->memory_data + 8 * (uint64)BH_GB;
-        }
 
 #if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
         /* Get stack info of current thread */
         stack_min_addr = os_thread_get_stack_boundary();
 #endif
 
-        if (memory_inst
-            && (mapped_mem_start_addr <= (uint8 *)sig_addr
-                && (uint8 *)sig_addr < mapped_mem_end_addr)) {
-            /* The address which causes segmentation fault is inside
-               the memory instance's guard regions */
+        if (is_sig_addr_in_guard_pages(sig_addr, module_inst)) {
             wasm_set_exception(module_inst, "out of bounds memory access");
             os_longjmp(jmpbuf_node->jmpbuf, 1);
         }
@@ -340,16 +351,7 @@ runtime_exception_handler(EXCEPTION_POINTERS *exce_info)
         && (jmpbuf_node = exec_env_tls->jmpbuf_stack_top)) {
         module_inst = (WASMModuleInstance *)exec_env_tls->module_inst;
         if (ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
-            /* Get the default memory instance */
-            memory_inst = wasm_get_default_memory(module_inst);
-            if (memory_inst) {
-                mapped_mem_start_addr = memory_inst->memory_data;
-                mapped_mem_end_addr =
-                    memory_inst->memory_data + 8 * (uint64)BH_GB;
-            }
-
-            if (memory_inst && mapped_mem_start_addr <= (uint8 *)sig_addr
-                && (uint8 *)sig_addr < mapped_mem_end_addr) {
+            if (is_sig_addr_in_guard_pages(sig_addr, module_inst)) {
                 /* The address which causes segmentation fault is inside
                    the memory instance's guard regions.
                    Set exception and let the wasm func continue to run, when

+ 1 - 0
core/iwasm/compilation/aot.c

@@ -540,6 +540,7 @@ aot_create_comp_data(WASMModule *module, const char *target_arch,
     /* TODO: create import memories */
 
     /* Allocate memory for memory array, reserve one AOTMemory space at least */
+    /* TODO: multi-memory */
     if (!comp_data->memory_count)
         comp_data->memory_count = 1;
 

+ 1 - 0
core/iwasm/compilation/aot_emit_memory.c

@@ -895,6 +895,7 @@ aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
 
     POP_PAGE_COUNT(delta);
 
+    /* TODO: multi-memory aot_enlarge_memory_with_idx() */
     /* Function type of aot_enlarge_memory() */
     param_types[0] = INT8_PTR_TYPE;
     param_types[1] = I32_TYPE;

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

@@ -2790,6 +2790,15 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
                 bh_assert(vendor_sys);
                 bh_memcpy_s(default_arch, sizeof(default_arch), default_triple,
                             (uint32)(vendor_sys - default_triple));
+                /**
+                 * On Mac M[1-9]+ LLVM will report arm64 as the
+                 * architecture, for the purposes of wamr this is the
+                 * same as aarch64v8 so we'll normalize it here.
+                 */
+                if (!strcmp(default_arch, "arm64")) {
+                    bh_strcpy_s(default_arch, sizeof(default_arch),
+                                "aarch64v8");
+                }
                 arch1 = default_arch;
 
                 LLVMDisposeMessage(default_triple);

+ 1 - 0
core/iwasm/fast-jit/fe/jit_emit_memory.c

@@ -602,6 +602,7 @@ jit_compile_op_memory_grow(JitCompContext *cc, uint32 mem_idx)
     args[0] = get_module_inst_reg(cc->jit_frame);
     args[1] = inc_page_count;
 
+    /* TODO: multi-memory wasm_enlarge_memory_with_idx() */
     if (!jit_emit_callnative(cc, wasm_enlarge_memory, grow_res, args, 2)) {
         goto fail;
     }

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

@@ -94,6 +94,14 @@ extern "C" {
 #define SHARED_MEMORY_FLAG 0x02
 #define MEMORY64_FLAG 0x04
 
+/**
+ * In the multi-memory proposal, the memarg in loads and stores are
+ * reinterpreted as a bitfield, bit 6 serves as a flag indicating the presence
+ * of the optional memory index, if it is set, then an i32 memory index follows
+ * after the alignment bitfield
+ */
+#define OPT_MEMIDX_FLAG 0x40
+
 #define DEFAULT_NUM_BYTES_PER_PAGE 65536
 #define DEFAULT_MAX_PAGES 65536
 #define DEFAULT_MEM64_MAX_PAGES UINT32_MAX

+ 105 - 51
core/iwasm/interpreter/wasm_interp_classic.c

@@ -697,6 +697,44 @@ wasm_interp_get_frame_ref(WASMInterpFrame *frame)
 #define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res)
 #endif
 
+#if WASM_ENABLE_MULTI_MEMORY != 0
+/* If the current memidx differs than the last cached one,
+ * update memory related information */
+#define read_leb_memidx(p, p_end, res)                        \
+    do {                                                      \
+        read_leb_uint32(p, p_end, res);                       \
+        if (res != memidx_cached) {                           \
+            memory = wasm_get_memory_with_idx(module, res);   \
+            linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); \
+            memidx_cached = res;                              \
+        }                                                     \
+    } while (0)
+/* First read the alignment, then if it has flag indicating following memidx,
+ * read and update memory related information, if it differs than the
+ * last(cached) one. If it doesn't have flag reset the
+ * memory instance to the default memories[0] */
+#define read_leb_memarg(p, p_end, res)                         \
+    do {                                                       \
+        read_leb_uint32(p, p_end, res);                        \
+        if (!(res & OPT_MEMIDX_FLAG))                          \
+            memidx = 0;                                        \
+        else                                                   \
+            read_leb_uint32(p, p_end, memidx);                 \
+        if (memidx != memidx_cached) {                         \
+            memory = wasm_get_memory_with_idx(module, memidx); \
+            linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory);  \
+            memidx_cached = memidx;                            \
+        }                                                      \
+    } while (0)
+#else
+#define read_leb_memarg(p, p_end, res)  \
+    do {                                \
+        read_leb_uint32(p, p_end, res); \
+        (void)res;                      \
+    } while (0)
+#define read_leb_memidx(p, p_end, res) read_leb_memarg(p, p_end, res)
+#endif
+
 #if WASM_ENABLE_LABELS_AS_VALUES == 0
 #define RECOVER_FRAME_IP_END() frame_ip_end = wasm_get_func_code_end(cur_func)
 #else
@@ -1567,6 +1605,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
     if (memory)
         is_memory64 = memory->is_memory64;
 #endif
+#if WASM_ENABLE_MULTI_MEMORY != 0
+    uint32 memidx = 0;
+    uint32 memidx_cached = (uint32)-1;
+#endif
 
 #if WASM_ENABLE_DEBUG_INTERP != 0
     uint8 *frame_ip_orig = NULL;
@@ -4292,13 +4334,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(4);
                 PUSH_I32(LOAD_I32(maddr));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4308,13 +4349,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(8);
                 PUSH_I64(LOAD_I64(maddr));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4323,13 +4363,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I32(sign_ext_8_32(*(int8 *)maddr));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4338,13 +4377,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I32((uint32)(*(uint8 *)maddr));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4353,13 +4391,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I32(sign_ext_16_32(LOAD_I16(maddr)));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4368,13 +4405,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I32((uint32)(LOAD_U16(maddr)));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4383,13 +4419,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I64(sign_ext_8_64(*(int8 *)maddr));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4398,13 +4433,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I64((uint64)(*(uint8 *)maddr));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4413,13 +4447,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I64(sign_ext_16_64(LOAD_I16(maddr)));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4428,13 +4461,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I64((uint64)(LOAD_U16(maddr)));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4443,14 +4475,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                opcode = *(frame_ip - 1);
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(4);
                 PUSH_I64(sign_ext_32_64(LOAD_I32(maddr)));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4459,13 +4489,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 addr = POP_MEM_OFFSET();
                 CHECK_MEMORY_OVERFLOW(4);
                 PUSH_I64((uint64)(LOAD_U32(maddr)));
                 CHECK_READ_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4476,7 +4505,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 frame_sp--;
                 addr = POP_MEM_OFFSET();
@@ -4491,7 +4520,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     STORE_U32(maddr, frame_sp[1]);
                 }
                 CHECK_WRITE_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4501,7 +4529,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 flags;
                 mem_offset_t offset, addr;
 
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 frame_sp -= 2;
                 addr = POP_MEM_OFFSET();
@@ -4519,7 +4547,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                                     GET_I64_FROM_ADDR(frame_sp + 1));
                 }
                 CHECK_WRITE_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4531,7 +4558,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint32 sval;
 
                 opcode = *(frame_ip - 1);
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 sval = (uint32)POP_I32();
                 addr = POP_MEM_OFFSET();
@@ -4545,7 +4572,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     STORE_U16(maddr, (uint16)sval);
                 }
                 CHECK_WRITE_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
@@ -4558,7 +4584,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 uint64 sval;
 
                 opcode = *(frame_ip - 1);
-                read_leb_uint32(frame_ip, frame_ip_end, flags);
+                read_leb_memarg(frame_ip, frame_ip_end, flags);
                 read_leb_mem_offset(frame_ip, frame_ip_end, offset);
                 sval = (uint64)POP_I64();
                 addr = POP_MEM_OFFSET();
@@ -4576,29 +4602,27 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     STORE_U32(maddr, (uint32)sval);
                 }
                 CHECK_WRITE_WATCHPOINT(addr, offset);
-                (void)flags;
                 HANDLE_OP_END();
             }
 
             /* memory size and memory grow instructions */
             HANDLE_OP(WASM_OP_MEMORY_SIZE)
             {
-                uint32 reserved;
-                read_leb_uint32(frame_ip, frame_ip_end, reserved);
+                uint32 mem_idx;
+                read_leb_memidx(frame_ip, frame_ip_end, mem_idx);
                 PUSH_PAGE_COUNT(memory->cur_page_count);
-                (void)reserved;
                 HANDLE_OP_END();
             }
 
             HANDLE_OP(WASM_OP_MEMORY_GROW)
             {
-                uint32 reserved, delta,
-                    prev_page_count = memory->cur_page_count;
+                uint32 mem_idx, delta, prev_page_count;
 
-                read_leb_uint32(frame_ip, frame_ip_end, reserved);
+                read_leb_memidx(frame_ip, frame_ip_end, mem_idx);
+                prev_page_count = memory->cur_page_count;
                 delta = (uint32)POP_PAGE_COUNT();
 
-                if (!wasm_enlarge_memory(module, delta)) {
+                if (!wasm_enlarge_memory_with_idx(module, delta, mem_idx)) {
                     /* failed to memory.grow, return -1 */
                     PUSH_PAGE_COUNT(-1);
                 }
@@ -4614,7 +4638,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 #endif
                 }
 
-                (void)reserved;
                 HANDLE_OP_END();
             }
 
@@ -5610,8 +5633,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                         uint8 *data;
 
                         read_leb_uint32(frame_ip, frame_ip_end, segment);
+#if WASM_ENABLE_MULTI_MEMORY != 0
+                        read_leb_memidx(frame_ip, frame_ip_end, memidx);
+#else
                         /* skip memory index */
                         frame_ip++;
+#endif
 
                         bytes = (uint64)(uint32)POP_I32();
                         offset = (uint64)(uint32)POP_I32();
@@ -5660,33 +5687,54 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     {
                         mem_offset_t dst, src, len;
                         uint8 *mdst, *msrc;
+                        uint64 dlen;
 
-                        frame_ip += 2;
                         len = POP_MEM_OFFSET();
                         src = POP_MEM_OFFSET();
                         dst = POP_MEM_OFFSET();
 
+#if WASM_ENABLE_MULTI_MEMORY != 0
+                        /* dst memidx */
+                        read_leb_memidx(frame_ip, frame_ip_end, memidx);
+#else
+                        /* skip dst memidx */
+                        frame_ip += 1;
+#endif
 #if WASM_ENABLE_THREAD_MGR != 0
                         linear_mem_size = get_linear_mem_size();
 #endif
-
+                        /* dst boundary check */
 #ifndef OS_ENABLE_HW_BOUND_CHECK
-                        CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc);
                         CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst);
 #else
-                        if ((uint64)(uint32)src + len > linear_mem_size)
+                        if ((uint64)dst + len > linear_mem_size)
                             goto out_of_bounds;
-                        msrc = memory->memory_data + (uint32)src;
+                        mdst = memory->memory_data + dst;
+#endif
+                        dlen = linear_mem_size - dst;
 
-                        if ((uint64)(uint32)dst + len > linear_mem_size)
+#if WASM_ENABLE_MULTI_MEMORY != 0
+                        /* src memidx */
+                        read_leb_memidx(frame_ip, frame_ip_end, memidx);
+#else
+                        /* skip src memidx */
+                        frame_ip += 1;
+#endif
+#if WASM_ENABLE_THREAD_MGR != 0
+                        linear_mem_size = get_linear_mem_size();
+#endif
+                        /* src boundary check */
+#ifndef OS_ENABLE_HW_BOUND_CHECK
+                        CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc);
+#else
+                        if ((uint64)src + len > linear_mem_size)
                             goto out_of_bounds;
-                        mdst = memory->memory_data + (uint32)dst;
+                        msrc = memory->memory_data + src;
 #endif
 
-                        /* allowing the destination and source to overlap */
 #if WASM_ENABLE_MEMORY64 == 0
-                        bh_memmove_s(mdst, (uint32)(linear_mem_size - dst),
-                                     msrc, (uint32)len);
+                        /* allowing the destination and source to overlap */
+                        bh_memmove_s(mdst, (uint32)dlen, msrc, (uint32)len);
 #else
                         /* use memmove when memory64 is enabled since len
                            may be larger than UINT32_MAX */
@@ -5698,7 +5746,13 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     {
                         mem_offset_t dst, len;
                         uint8 fill_val, *mdst;
+
+#if WASM_ENABLE_MULTI_MEMORY != 0
+                        read_leb_memidx(frame_ip, frame_ip_end, memidx);
+#else
+                        /* skip memory index */
                         frame_ip++;
+#endif
 
                         len = POP_MEM_OFFSET();
                         fill_val = POP_I32();

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

@@ -3837,6 +3837,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr_ret = GET_OFFSET();
                 delta = (uint32)frame_lp[addr1];
 
+                /* TODO: multi-memory wasm_enlarge_memory_with_idx() */
                 if (!wasm_enlarge_memory(module, delta)) {
                     /* failed to memory.grow, return -1 */
                     frame_lp[addr_ret] = -1;

+ 71 - 36
core/iwasm/interpreter/wasm_loader.c

@@ -127,6 +127,17 @@ check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length,
 #define skip_leb_uint32(p, p_end) skip_leb(p)
 #define skip_leb_int32(p, p_end) skip_leb(p)
 #define skip_leb_mem_offset(p, p_end) skip_leb(p)
+#define skip_leb_memidx(p, p_end) skip_leb(p)
+#if WASM_ENABLE_MULTI_MEMORY == 0
+#define skip_leb_align(p, p_end) skip_leb(p)
+#else
+/* Skip the following memidx if applicable */
+#define skip_leb_align(p, p_end)       \
+    do {                               \
+        if (*p++ & OPT_MEMIDX_FLAG)    \
+            skip_leb_uint32(p, p_end); \
+    } while (0)
+#endif
 
 #define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p)
 #define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p)
@@ -173,6 +184,40 @@ check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length,
         res = (int32)res64;                                             \
     } while (0)
 
+#define read_leb_memidx(p, p_end, res) read_leb_uint32(p, p_end, res)
+#if WASM_ENABLE_MULTI_MEMORY != 0
+#define check_memidx(module, memidx)                                        \
+    do {                                                                    \
+        if (memidx >= module->import_memory_count + module->memory_count) { \
+            set_error_buf_v(error_buf, error_buf_size, "unknown memory %d", \
+                            memidx);                                        \
+            goto fail;                                                      \
+        }                                                                   \
+    } while (0)
+/* Bit 6(0x40) indicating the optional memidx, and reset bit 6 for
+ * alignment check */
+#define read_leb_memarg(p, p_end, res)                      \
+    do {                                                    \
+        read_leb_uint32(p, p_end, res);                     \
+        if (res & OPT_MEMIDX_FLAG) {                        \
+            res &= ~OPT_MEMIDX_FLAG;                        \
+            read_leb_uint32(p, p_end, memidx); /* memidx */ \
+            check_memidx(module, memidx);                   \
+        }                                                   \
+    } while (0)
+#else
+/* reserved byte 0x00 */
+#define check_memidx(module, memidx)                                        \
+    do {                                                                    \
+        (void)module;                                                       \
+        if (memidx != 0) {                                                  \
+            set_error_buf(error_buf, error_buf_size, "zero byte expected"); \
+            goto fail;                                                      \
+        }                                                                   \
+    } while (0)
+#define read_leb_memarg(p, p_end, res) read_leb_uint32(p, p_end, res)
+#endif
+
 static char *
 type2str(uint8 type)
 {
@@ -3288,11 +3333,13 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                     if (flags & 1)
                         read_leb_uint32(p, p_end, u32);
                     module->import_memory_count++;
+#if WASM_ENABLE_MULTI_MEMORY == 0
                     if (module->import_memory_count > 1) {
                         set_error_buf(error_buf, error_buf_size,
                                       "multiple memories");
                         return false;
                     }
+#endif
                     break;
 
 #if WASM_ENABLE_TAGS != 0
@@ -3903,11 +3950,14 @@ load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
     WASMMemory *memory;
 
     read_leb_uint32(p, p_end, memory_count);
+
+#if WASM_ENABLE_MULTI_MEMORY == 0
     /* a total of one memory is allowed */
     if (module->import_memory_count + memory_count > 1) {
         set_error_buf(error_buf, error_buf_size, "multiple memories");
         return false;
     }
+#endif
 
     if (memory_count) {
         module->memory_count = memory_count;
@@ -7258,13 +7308,13 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
             case WASM_OP_I64_STORE8:
             case WASM_OP_I64_STORE16:
             case WASM_OP_I64_STORE32:
-                skip_leb_uint32(p, p_end);     /* align */
+                skip_leb_align(p, p_end);      /* align */
                 skip_leb_mem_offset(p, p_end); /* offset */
                 break;
 
             case WASM_OP_MEMORY_SIZE:
             case WASM_OP_MEMORY_GROW:
-                skip_leb_uint32(p, p_end); /* 0x00 */
+                skip_leb_memidx(p, p_end); /* memidx */
                 break;
 
             case WASM_OP_I32_CONST:
@@ -7562,19 +7612,17 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
 #if WASM_ENABLE_BULK_MEMORY != 0
                     case WASM_OP_MEMORY_INIT:
                         skip_leb_uint32(p, p_end);
-                        /* skip memory idx */
-                        p++;
+                        skip_leb_memidx(p, p_end);
                         break;
                     case WASM_OP_DATA_DROP:
                         skip_leb_uint32(p, p_end);
                         break;
                     case WASM_OP_MEMORY_COPY:
-                        /* skip two memory idx */
-                        p += 2;
+                        skip_leb_memidx(p, p_end);
+                        skip_leb_memidx(p, p_end);
                         break;
                     case WASM_OP_MEMORY_FILL:
-                        /* skip memory idx */
-                        p++;
+                        skip_leb_memidx(p, p_end);
                         break;
 #endif /* WASM_ENABLE_BULK_MEMORY */
 #if WASM_ENABLE_REF_TYPES != 0
@@ -7701,7 +7749,6 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
 #if WASM_ENABLE_SHARED_MEMORY != 0
             case WASM_OP_ATOMIC_PREFIX:
             {
-                /* TODO: memory64 offset type changes */
                 uint32 opcode1;
 
                 /* atomic_op (u32_leb) + memarg (2 u32_leb) */
@@ -10876,6 +10923,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
 #else
     mem_offset_type = VALUE_TYPE_I32;
 #endif
+    uint32 memidx;
 
     global_count = module->import_global_count + module->global_count;
 
@@ -13155,7 +13203,7 @@ re_scan:
                 }
 #endif
                 CHECK_MEMORY();
-                read_leb_uint32(p, p_end, align);          /* align */
+                read_leb_memarg(p, p_end, align);          /* align */
                 read_leb_mem_offset(p, p_end, mem_offset); /* offset */
                 if (!check_memory_access_align(opcode, align, error_buf,
                                                error_buf_size)) {
@@ -13221,12 +13269,8 @@ re_scan:
 
             case WASM_OP_MEMORY_SIZE:
                 CHECK_MEMORY();
-                /* reserved byte 0x00 */
-                if (*p++ != 0x00) {
-                    set_error_buf(error_buf, error_buf_size,
-                                  "zero byte expected");
-                    goto fail;
-                }
+                read_leb_uint32(p, p_end, memidx);
+                check_memidx(module, memidx);
                 PUSH_PAGE_COUNT();
 
                 module->possible_memory_grow = true;
@@ -13237,12 +13281,8 @@ re_scan:
 
             case WASM_OP_MEMORY_GROW:
                 CHECK_MEMORY();
-                /* reserved byte 0x00 */
-                if (*p++ != 0x00) {
-                    set_error_buf(error_buf, error_buf_size,
-                                  "zero byte expected");
-                    goto fail;
-                }
+                read_leb_uint32(p, p_end, memidx);
+                check_memidx(module, memidx);
                 POP_AND_PUSH(mem_offset_type, mem_offset_type);
 
                 module->possible_memory_grow = true;
@@ -14594,8 +14634,8 @@ re_scan:
                             && module->memory_count == 0)
                             goto fail_unknown_memory;
 
-                        if (*p++ != 0x00)
-                            goto fail_zero_byte_expected;
+                        read_leb_uint32(p, p_end, memidx);
+                        check_memidx(module, memidx);
 
                         if (data_seg_idx >= module->data_seg_count) {
                             set_error_buf_v(error_buf, error_buf_size,
@@ -14644,10 +14684,11 @@ re_scan:
                     case WASM_OP_MEMORY_COPY:
                     {
                         CHECK_BUF(p, p_end, sizeof(int16));
-                        /* both src and dst memory index should be 0 */
-                        if (*(int16 *)p != 0x0000)
-                            goto fail_zero_byte_expected;
-                        p += 2;
+                        /* check both src and dst memory index */
+                        read_leb_uint32(p, p_end, memidx);
+                        check_memidx(module, memidx);
+                        read_leb_uint32(p, p_end, memidx);
+                        check_memidx(module, memidx);
 
                         if (module->import_memory_count == 0
                             && module->memory_count == 0)
@@ -14666,9 +14707,8 @@ re_scan:
                     }
                     case WASM_OP_MEMORY_FILL:
                     {
-                        if (*p++ != 0x00) {
-                            goto fail_zero_byte_expected;
-                        }
+                        read_leb_uint32(p, p_end, memidx);
+                        check_memidx(module, memidx);
                         if (module->import_memory_count == 0
                             && module->memory_count == 0) {
                             goto fail_unknown_memory;
@@ -14684,10 +14724,6 @@ re_scan:
 #endif
                         break;
                     }
-                    fail_zero_byte_expected:
-                        set_error_buf(error_buf, error_buf_size,
-                                      "zero byte expected");
-                        goto fail;
 
                     fail_unknown_memory:
                         set_error_buf(error_buf, error_buf_size,
@@ -14921,7 +14957,6 @@ re_scan:
 #if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0)
             case WASM_OP_SIMD_PREFIX:
             {
-                /* TODO: memory64 offset type changes */
                 uint32 opcode1;
 
 #if WASM_ENABLE_WAMR_COMPILER != 0

+ 68 - 35
core/iwasm/interpreter/wasm_mini_loader.c

@@ -62,6 +62,17 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
 #define skip_leb_uint32(p, p_end) skip_leb(p)
 #define skip_leb_int32(p, p_end) skip_leb(p)
 #define skip_leb_mem_offset(p, p_end) skip_leb(p)
+#define skip_leb_memidx(p, p_end) skip_leb(p)
+#if WASM_ENABLE_MULTI_MEMORY == 0
+#define skip_leb_align(p, p_end) skip_leb(p)
+#else
+/* Skip the following memidx if applicable */
+#define skip_leb_align(p, p_end)       \
+    do {                               \
+        if (*p++ & OPT_MEMIDX_FLAG)    \
+            skip_leb_uint32(p, p_end); \
+    } while (0)
+#endif
 
 static bool
 is_32bit_type(uint8 type)
@@ -132,6 +143,35 @@ is_byte_a_type(uint8 type)
 #else
 #define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res)
 #endif
+#define read_leb_memidx(p, p_end, res) read_leb_uint32(p, p_end, res)
+#if WASM_ENABLE_MULTI_MEMORY != 0
+#define check_memidx(module, memidx)                                     \
+    do {                                                                 \
+        bh_assert(memidx                                                 \
+                  < module->import_memory_count + module->memory_count); \
+        (void)memidx;                                                    \
+    } while (0)
+/* Bit 6 indicating the optional memidx, and reset bit 6 for
+ * alignment check */
+#define read_leb_memarg(p, p_end, res)                      \
+    do {                                                    \
+        read_leb_uint32(p, p_end, res);                     \
+        if (res & OPT_MEMIDX_FLAG) {                        \
+            res &= ~OPT_MEMIDX_FLAG;                        \
+            read_leb_uint32(p, p_end, memidx); /* memidx */ \
+            check_memidx(module, memidx);                   \
+        }                                                   \
+    } while (0)
+#else
+/* reserved byte 0x00 */
+#define check_memidx(module, memidx) \
+    do {                             \
+        (void)module;                \
+        bh_assert(memidx == 0);      \
+        (void)memidx;                \
+    } while (0)
+#define read_leb_memarg(p, p_end, res) read_leb_uint32(p, p_end, res)
+#endif
 
 static void *
 loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size)
@@ -882,7 +922,9 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
                     if (flags & 1)
                         read_leb_uint32(p, p_end, u32);
                     module->import_memory_count++;
+#if WASM_ENABLE_MULTI_MEMORY != 0
                     bh_assert(module->import_memory_count <= 1);
+#endif
                     break;
 
                 case IMPORT_KIND_GLOBAL: /* import global */
@@ -1223,7 +1265,9 @@ load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
     WASMMemory *memory;
 
     read_leb_uint32(p, p_end, memory_count);
+#if WASM_ENABLE_MULTI_MEMORY != 0
     bh_assert(module->import_memory_count + memory_count <= 1);
+#endif
 
     if (memory_count) {
         module->memory_count = memory_count;
@@ -3585,13 +3629,13 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
             case WASM_OP_I64_STORE8:
             case WASM_OP_I64_STORE16:
             case WASM_OP_I64_STORE32:
-                skip_leb_uint32(p, p_end);     /* align */
+                skip_leb_align(p, p_end);      /* align */
                 skip_leb_mem_offset(p, p_end); /* offset */
                 break;
 
             case WASM_OP_MEMORY_SIZE:
             case WASM_OP_MEMORY_GROW:
-                skip_leb_uint32(p, p_end); /* 0x00 */
+                skip_leb_memidx(p, p_end); /* memidx */
                 break;
 
             case WASM_OP_I32_CONST:
@@ -3758,19 +3802,17 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
 #if WASM_ENABLE_BULK_MEMORY != 0
                     case WASM_OP_MEMORY_INIT:
                         skip_leb_uint32(p, p_end);
-                        /* skip memory idx */
-                        p++;
+                        skip_leb_memidx(p, p_end);
                         break;
                     case WASM_OP_DATA_DROP:
                         skip_leb_uint32(p, p_end);
                         break;
                     case WASM_OP_MEMORY_COPY:
-                        /* skip two memory idx */
-                        p += 2;
+                        skip_leb_memidx(p, p_end);
+                        skip_leb_memidx(p, p_end);
                         break;
                     case WASM_OP_MEMORY_FILL:
-                        /* skip memory idx */
-                        p++;
+                        skip_leb_memidx(p, p_end);
                         break;
 #endif
 #if WASM_ENABLE_REF_TYPES != 0
@@ -5905,7 +5947,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
     uint8 *param_types, *local_types, local_type, global_type, mem_offset_type;
     BlockType func_block_type;
     uint16 *local_offsets, local_offset;
-    uint32 count, local_idx, global_idx, u32, align, i;
+    uint32 count, local_idx, global_idx, u32, align, i, memidx;
     mem_offset_t mem_offset;
     int32 i32, i32_const = 0;
     int64 i64_const;
@@ -7267,7 +7309,7 @@ re_scan:
                 }
 #endif
                 CHECK_MEMORY();
-                read_leb_uint32(p, p_end, align);          /* align */
+                read_leb_memarg(p, p_end, align);          /* align */
                 read_leb_mem_offset(p, p_end, mem_offset); /* offset */
 #if WASM_ENABLE_FAST_INTERP != 0
                 emit_uint32(loader_ctx, mem_offset);
@@ -7329,9 +7371,8 @@ re_scan:
 
             case WASM_OP_MEMORY_SIZE:
                 CHECK_MEMORY();
-                /* reserved byte 0x00 */
-                bh_assert(*p == 0x00);
-                p++;
+                read_leb_memidx(p, p_end, memidx);
+                check_memidx(module, memidx);
                 PUSH_PAGE_COUNT();
 
                 module->possible_memory_grow = true;
@@ -7342,9 +7383,8 @@ re_scan:
 
             case WASM_OP_MEMORY_GROW:
                 CHECK_MEMORY();
-                /* reserved byte 0x00 */
-                bh_assert(*p == 0x00);
-                p++;
+                read_leb_memidx(p, p_end, memidx);
+                check_memidx(module, memidx);
                 POP_AND_PUSH(mem_offset_type, mem_offset_type);
 
                 module->possible_memory_grow = true;
@@ -7682,16 +7722,13 @@ re_scan:
 #if WASM_ENABLE_BULK_MEMORY != 0
                     case WASM_OP_MEMORY_INIT:
                     {
+                        CHECK_MEMORY();
                         read_leb_uint32(p, p_end, segment_index);
 #if WASM_ENABLE_FAST_INTERP != 0
                         emit_uint32(loader_ctx, segment_index);
 #endif
-                        bh_assert(module->import_memory_count
-                                      + module->memory_count
-                                  > 0);
-
-                        bh_assert(*p == 0x00);
-                        p++;
+                        read_leb_memidx(p, p_end, memidx);
+                        check_memidx(module, memidx);
 
                         bh_assert(segment_index < module->data_seg_count);
                         bh_assert(module->data_seg_count1 > 0);
@@ -7719,14 +7756,13 @@ re_scan:
                     }
                     case WASM_OP_MEMORY_COPY:
                     {
+                        CHECK_MEMORY();
                         CHECK_BUF(p, p_end, sizeof(int16));
-                        /* both src and dst memory index should be 0 */
-                        bh_assert(*(int16 *)p == 0x0000);
-                        p += 2;
-
-                        bh_assert(module->import_memory_count
-                                      + module->memory_count
-                                  > 0);
+                        /* check both src and dst memory index */
+                        read_leb_memidx(p, p_end, memidx);
+                        check_memidx(module, memidx);
+                        read_leb_memidx(p, p_end, memidx);
+                        check_memidx(module, memidx);
 
                         POP_MEM_OFFSET();
                         POP_MEM_OFFSET();
@@ -7738,12 +7774,9 @@ re_scan:
                     }
                     case WASM_OP_MEMORY_FILL:
                     {
-                        bh_assert(*p == 0);
-                        p++;
-
-                        bh_assert(module->import_memory_count
-                                      + module->memory_count
-                                  > 0);
+                        CHECK_MEMORY();
+                        read_leb_memidx(p, p_end, memidx);
+                        check_memidx(module, memidx);
 
                         POP_MEM_OFFSET();
                         POP_I32();

+ 159 - 100
core/iwasm/interpreter/wasm_runtime.c

@@ -194,114 +194,119 @@ memory_instantiate(WASMModuleInstance *module_inst, WASMModuleInstance *parent,
     default_max_page =
         memory->is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES;
 
-    if (heap_size > 0 && module_inst->module->malloc_function != (uint32)-1
-        && module_inst->module->free_function != (uint32)-1) {
-        /* Disable app heap, use malloc/free function exported
-           by wasm app to allocate/free memory instead */
-        heap_size = 0;
-    }
-
-    /* If initial memory is the largest size allowed, disallowing insert host
-     * managed heap */
-    if (heap_size > 0
-        && heap_offset == GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64)) {
-        set_error_buf(error_buf, error_buf_size,
-                      "failed to insert app heap into linear memory, "
-                      "try using `--heap-size=0` option");
-        return NULL;
-    }
+    /* The app heap should be in the default memory */
+    if (memory_idx == 0) {
+        if (heap_size > 0 && module_inst->module->malloc_function != (uint32)-1
+            && module_inst->module->free_function != (uint32)-1) {
+            /* Disable app heap, use malloc/free function exported
+               by wasm app to allocate/free memory instead */
+            heap_size = 0;
+        }
 
-    if (init_page_count == max_page_count && init_page_count == 1) {
-        /* If only one page and at most one page, we just append
-           the app heap to the end of linear memory, enlarge the
-           num_bytes_per_page, and don't change the page count */
-        heap_offset = num_bytes_per_page;
-        num_bytes_per_page += heap_size;
-        if (num_bytes_per_page < heap_size) {
+        /* If initial memory is the largest size allowed, disallowing insert
+         * host managed heap */
+        if (heap_size > 0
+            && heap_offset == GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64)) {
             set_error_buf(error_buf, error_buf_size,
                           "failed to insert app heap into linear memory, "
                           "try using `--heap-size=0` option");
             return NULL;
         }
-    }
-    else if (heap_size > 0) {
-        if (init_page_count == max_page_count && init_page_count == 0) {
-            /* If the memory data size is always 0, we resize it to
-               one page for app heap */
-            num_bytes_per_page = heap_size;
-            heap_offset = 0;
-            inc_page_count = 1;
+
+        if (init_page_count == max_page_count && init_page_count == 1) {
+            /* If only one page and at most one page, we just append
+               the app heap to the end of linear memory, enlarge the
+               num_bytes_per_page, and don't change the page count */
+            heap_offset = num_bytes_per_page;
+            num_bytes_per_page += heap_size;
+            if (num_bytes_per_page < heap_size) {
+                set_error_buf(error_buf, error_buf_size,
+                              "failed to insert app heap into linear memory, "
+                              "try using `--heap-size=0` option");
+                return NULL;
+            }
         }
-        else if (module->aux_heap_base_global_index != (uint32)-1
-                 && module->aux_heap_base
-                        < (uint64)num_bytes_per_page * init_page_count) {
-            /* Insert app heap before __heap_base */
-            aux_heap_base = module->aux_heap_base;
-            bytes_of_last_page = aux_heap_base % num_bytes_per_page;
-            if (bytes_of_last_page == 0)
-                bytes_of_last_page = num_bytes_per_page;
-            bytes_to_page_end = num_bytes_per_page - bytes_of_last_page;
-            inc_page_count =
-                (heap_size - bytes_to_page_end + num_bytes_per_page - 1)
-                / num_bytes_per_page;
-            heap_offset = aux_heap_base;
-            aux_heap_base += heap_size;
-
-            bytes_of_last_page = aux_heap_base % num_bytes_per_page;
-            if (bytes_of_last_page == 0)
-                bytes_of_last_page = num_bytes_per_page;
-            bytes_to_page_end = num_bytes_per_page - bytes_of_last_page;
-            if (bytes_to_page_end < 1 * BH_KB) {
-                aux_heap_base += 1 * BH_KB;
-                inc_page_count++;
+        else if (heap_size > 0) {
+            if (init_page_count == max_page_count && init_page_count == 0) {
+                /* If the memory data size is always 0, we resize it to
+                   one page for app heap */
+                num_bytes_per_page = heap_size;
+                heap_offset = 0;
+                inc_page_count = 1;
             }
+            else if (module->aux_heap_base_global_index != (uint32)-1
+                     && module->aux_heap_base
+                            < (uint64)num_bytes_per_page * init_page_count) {
+                /* Insert app heap before __heap_base */
+                aux_heap_base = module->aux_heap_base;
+                bytes_of_last_page = aux_heap_base % num_bytes_per_page;
+                if (bytes_of_last_page == 0)
+                    bytes_of_last_page = num_bytes_per_page;
+                bytes_to_page_end = num_bytes_per_page - bytes_of_last_page;
+                inc_page_count =
+                    (heap_size - bytes_to_page_end + num_bytes_per_page - 1)
+                    / num_bytes_per_page;
+                heap_offset = aux_heap_base;
+                aux_heap_base += heap_size;
+
+                bytes_of_last_page = aux_heap_base % num_bytes_per_page;
+                if (bytes_of_last_page == 0)
+                    bytes_of_last_page = num_bytes_per_page;
+                bytes_to_page_end = num_bytes_per_page - bytes_of_last_page;
+                if (bytes_to_page_end < 1 * BH_KB) {
+                    aux_heap_base += 1 * BH_KB;
+                    inc_page_count++;
+                }
 
-            /* Adjust __heap_base global value */
-            global_idx = module->aux_heap_base_global_index;
-            bh_assert(module_inst->e->globals
-                      && global_idx < module_inst->e->global_count);
-            global_addr = module_inst->global_data
-                          + module_inst->e->globals[global_idx].data_offset;
+                /* Adjust __heap_base global value */
+                global_idx = module->aux_heap_base_global_index;
+                bh_assert(module_inst->e->globals
+                          && global_idx < module_inst->e->global_count);
+                global_addr = module_inst->global_data
+                              + module_inst->e->globals[global_idx].data_offset;
 #if WASM_ENABLE_MEMORY64 != 0
-            if (memory->is_memory64) {
-                /* For memory64, the global value should be i64 */
-                *(uint64 *)global_addr = aux_heap_base;
-            }
-            else
+                if (memory->is_memory64) {
+                    /* For memory64, the global value should be i64 */
+                    *(uint64 *)global_addr = aux_heap_base;
+                }
+                else
 #endif
-            {
-                /* For memory32, the global value should be i32 */
-                *(uint32 *)global_addr = (uint32)aux_heap_base;
+                {
+                    /* For memory32, the global value should be i32 */
+                    *(uint32 *)global_addr = (uint32)aux_heap_base;
+                }
+                LOG_VERBOSE("Reset __heap_base global to %" PRIu64,
+                            aux_heap_base);
+            }
+            else {
+                /* Insert app heap before new page */
+                inc_page_count =
+                    (heap_size + num_bytes_per_page - 1) / num_bytes_per_page;
+                heap_offset = (uint64)num_bytes_per_page * init_page_count;
+                heap_size = (uint64)num_bytes_per_page * inc_page_count;
+                if (heap_size > 0)
+                    heap_size -= 1 * BH_KB;
+            }
+            init_page_count += inc_page_count;
+            max_page_count += inc_page_count;
+            if (init_page_count > default_max_page) {
+                set_error_buf(error_buf, error_buf_size,
+                              "failed to insert app heap into linear memory, "
+                              "try using `--heap-size=0` option");
+                return NULL;
             }
-            LOG_VERBOSE("Reset __heap_base global to %" PRIu64, aux_heap_base);
-        }
-        else {
-            /* Insert app heap before new page */
-            inc_page_count =
-                (heap_size + num_bytes_per_page - 1) / num_bytes_per_page;
-            heap_offset = (uint64)num_bytes_per_page * init_page_count;
-            heap_size = (uint64)num_bytes_per_page * inc_page_count;
-            if (heap_size > 0)
-                heap_size -= 1 * BH_KB;
-        }
-        init_page_count += inc_page_count;
-        max_page_count += inc_page_count;
-        if (init_page_count > default_max_page) {
-            set_error_buf(error_buf, error_buf_size,
-                          "failed to insert app heap into linear memory, "
-                          "try using `--heap-size=0` option");
-            return NULL;
-        }
 
-        if (max_page_count > default_max_page)
-            max_page_count = default_max_page;
+            if (max_page_count > default_max_page)
+                max_page_count = default_max_page;
+        }
     }
 
     LOG_VERBOSE("Memory instantiate:");
     LOG_VERBOSE("  page bytes: %u, init pages: %u, max pages: %u",
                 num_bytes_per_page, init_page_count, max_page_count);
-    LOG_VERBOSE("  heap offset: %" PRIu64 ", heap size: %u\n", heap_offset,
-                heap_size);
+    if (memory_idx == 0)
+        LOG_VERBOSE("  heap offset: %" PRIu64 ", heap size: %u\n", heap_offset,
+                    heap_size);
 
     max_memory_data_size = (uint64)num_bytes_per_page * max_page_count;
     bh_assert(max_memory_data_size
@@ -326,12 +331,14 @@ memory_instantiate(WASMModuleInstance *module_inst, WASMModuleInstance *parent,
     memory->max_page_count = max_page_count;
     memory->memory_data_size = memory_data_size;
 
-    memory->heap_data = memory->memory_data + heap_offset;
-    memory->heap_data_end = memory->heap_data + heap_size;
-    memory->memory_data_end = memory->memory_data + memory_data_size;
+    if (memory_idx == 0) {
+        memory->heap_data = memory->memory_data + heap_offset;
+        memory->heap_data_end = memory->heap_data + heap_size;
+        memory->memory_data_end = memory->memory_data + memory_data_size;
+    }
 
     /* Initialize heap */
-    if (heap_size > 0) {
+    if (memory_idx == 0 && heap_size > 0) {
         uint32 heap_struct_size = mem_allocator_get_heap_struct_size();
 
         if (!(memory->heap_handle = runtime_malloc(
@@ -361,7 +368,7 @@ memory_instantiate(WASMModuleInstance *module_inst, WASMModuleInstance *parent,
     return memory;
 
 fail2:
-    if (heap_size > 0)
+    if (memory_idx == 0 && heap_size > 0)
         wasm_runtime_free(memory->heap_handle);
 fail1:
     if (memory->memory_data)
@@ -1351,7 +1358,45 @@ export_globals_instantiate(const WASMModule *module,
     bh_assert((uint32)(export_global - export_globals) == export_glob_count);
     return export_globals;
 }
-#endif
+
+#if WASM_ENABLE_MULTI_MEMORY != 0
+static void
+export_memories_deinstantiate(WASMExportMemInstance *memories)
+{
+    if (memories)
+        wasm_runtime_free(memories);
+}
+
+static WASMExportMemInstance *
+export_memories_instantiate(const WASMModule *module,
+                            WASMModuleInstance *module_inst,
+                            uint32 export_mem_count, char *error_buf,
+                            uint32 error_buf_size)
+{
+    WASMExportMemInstance *export_memories, *export_memory;
+    WASMExport *export = module->exports;
+    uint32 i;
+    uint64 total_size =
+        sizeof(WASMExportMemInstance) * (uint64)export_mem_count;
+
+    if (!(export_memory = export_memories =
+              runtime_malloc(total_size, error_buf, error_buf_size))) {
+        return NULL;
+    }
+
+    for (i = 0; i < module->export_count; i++, export ++)
+        if (export->kind == EXPORT_KIND_MEMORY) {
+            export_memory->name = export->name;
+            export_memory->memory = module_inst->memories[export->index];
+            export_memory++;
+        }
+
+    bh_assert((uint32)(export_memory - export_memories) == export_mem_count);
+    return export_memories;
+}
+#endif /* end of if WASM_ENABLE_MULTI_MEMORY != 0 */
+
+#endif /* end of if WASM_ENABLE_MULTI_MODULE != 0 */
 
 static WASMFunctionInstance *
 lookup_post_instantiate_func(WASMModuleInstance *module_inst,
@@ -2387,6 +2432,12 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent,
                      module, module_inst, module_inst->export_global_count,
                      error_buf, error_buf_size)))
 #endif
+#if WASM_ENABLE_MULTI_MODULE != 0 && WASM_ENABLE_MULTI_MEMORY != 0
+        || (module_inst->export_memory_count > 0
+            && !(module_inst->export_memories = export_memories_instantiate(
+                     module, module_inst, module_inst->export_memory_count,
+                     error_buf, error_buf_size)))
+#endif
 #if WASM_ENABLE_JIT != 0
         || (module_inst->e->function_count > 0
             && !init_func_ptrs(module_inst, module, error_buf, error_buf_size))
@@ -3189,6 +3240,10 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst)
     export_globals_deinstantiate(module_inst->export_globals);
 #endif
 
+#if WASM_ENABLE_MULTI_MODULE != 0 && WASM_ENABLE_MULTI_MEMORY != 0
+    export_memories_deinstantiate(module_inst->export_memories);
+#endif
+
 #if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0
     wasm_externref_cleanup((WASMModuleInstanceCommon *)module_inst);
 #endif
@@ -3251,12 +3306,16 @@ wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name)
 WASMMemoryInstance *
 wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name)
 {
-    /**
-     * using a strong assumption that one module instance only has
-     * one memory instance
-     */
+#if WASM_ENABLE_MULTI_MEMORY != 0
+    uint32 i;
+    for (i = 0; i < module_inst->export_memory_count; i++)
+        if (!strcmp(module_inst->export_memories[i].name, name))
+            return module_inst->export_memories[i].memory;
+    return NULL;
+#else
     (void)module_inst->export_memories;
     return module_inst->memories[0];
+#endif
 }
 
 WASMTableInstance *

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

@@ -620,9 +620,16 @@ wasm_check_app_addr_and_convert(WASMModuleInstance *module_inst, bool is_str,
 WASMMemoryInstance *
 wasm_get_default_memory(WASMModuleInstance *module_inst);
 
+WASMMemoryInstance *
+wasm_get_memory_with_idx(WASMModuleInstance *module_inst, uint32 index);
+
 bool
 wasm_enlarge_memory(WASMModuleInstance *module_inst, uint32 inc_page_count);
 
+bool
+wasm_enlarge_memory_with_idx(WASMModuleInstance *module_inst,
+                             uint32 inc_page_count, uint32 memidx);
+
 bool
 wasm_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx,
                    uint32 argc, uint32 argv[]);

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

@@ -14,7 +14,7 @@ import time
 
 """
 The script itself has to be put under the same directory with the "spec".
-To run a single non-GC and non-memory64 case with interpreter mode:
+To run a single non-GC case with interpreter mode:
   cd workspace
   python3 runtest.py --wast2wasm wabt/bin/wat2wasm --interpreter iwasm \
     spec/test/core/xxx.wast
@@ -22,7 +22,7 @@ To run a single non-GC case with aot mode:
   cd workspace
   python3 runtest.py --aot --wast2wasm wabt/bin/wat2wasm --interpreter iwasm \
     --aot-compiler wamrc spec/test/core/xxx.wast
-To run a single GC case or single memory64 case:
+To run a single GC case case:
   cd workspace
   python3 runtest.py --wast2wasm spec/interpreter/wasm --interpreter iwasm \
     --aot-compiler wamrc --gc spec/test/core/xxx.wast
@@ -79,6 +79,7 @@ def ignore_the_case(
     simd_flag=False,
     gc_flag=False,
     memory64_flag=False,
+    multi_memory_flag=False,
     xip_flag=False,
     eh_flag=False,
     qemu_flag=False,
@@ -165,6 +166,7 @@ def test_case(
     verbose_flag=True,
     gc_flag=False,
     memory64_flag=False,
+    multi_memory_flag=False,
     qemu_flag=False,
     qemu_firmware="",
     log="",
@@ -223,6 +225,9 @@ def test_case(
     if memory64_flag:
         CMD.append("--memory64")
 
+    if multi_memory_flag:
+        CMD.append("--multi-memory")
+
     if log != "":
         CMD.append("--log-dir")
         CMD.append(log)
@@ -291,6 +296,7 @@ def test_suite(
     verbose_flag=True,
     gc_flag=False,
     memory64_flag=False,
+    multi_memory_flag=False,
     parl_flag=False,
     qemu_flag=False,
     qemu_firmware="",
@@ -316,6 +322,10 @@ def test_suite(
         eh_case_list_include = [test for test in eh_case_list if test.stem in ["throw", "tag", "try_catch", "rethrow", "try_delegate"]]
         case_list.extend(eh_case_list_include)
 
+    if multi_memory_flag:
+        multi_memory_list = sorted(suite_path.glob("multi-memory/*.wast"))
+        case_list.extend(multi_memory_list)
+
     # ignore based on command line options
     filtered_case_list = []
     for case_path in case_list:
@@ -330,6 +340,7 @@ def test_suite(
             simd_flag,
             gc_flag,
             memory64_flag,
+            multi_memory_flag,
             xip_flag,
             eh_flag,
             qemu_flag,
@@ -366,6 +377,7 @@ def test_suite(
                         verbose_flag,
                         gc_flag,
                         memory64_flag,
+                        multi_memory_flag,
                         qemu_flag,
                         qemu_firmware,
                         log,
@@ -408,6 +420,7 @@ def test_suite(
                     verbose_flag,
                     gc_flag,
                     memory64_flag,
+                    multi_memory_flag,
                     qemu_flag,
                     qemu_firmware,
                     log,
@@ -546,6 +559,13 @@ def main():
         dest="memory64_flag",
         help="Running with memory64 feature",
     )
+    parser.add_argument(
+        "--multi-memory",
+        action="store_true",
+        default=False,
+        dest="multi_memory_flag",
+        help="Running with multi-memory feature",
+    )
     parser.add_argument(
         "cases",
         metavar="path_to__case",
@@ -591,6 +611,7 @@ def main():
             options.verbose_flag,
             options.gc_flag,
             options.memory64_flag,
+            options.multi_memory_flag,
             options.parl_flag,
             options.qemu_flag,
             options.qemu_firmware,
@@ -619,6 +640,7 @@ def main():
                     options.verbose_flag,
                     options.gc_flag,
                     options.memory64_flag,
+                    options.multi_memory_flag,
                     options.qemu_flag,
                     options.qemu_firmware,
                     options.log,

+ 1022 - 0
tests/wamr-test-suites/spec-test-script/multi_memory_ignore_cases.patch

@@ -0,0 +1,1022 @@
+diff --git a/test/core/elem.wast b/test/core/elem.wast
+index 575ecef8..6eecab93 100644
+--- a/test/core/elem.wast
++++ b/test/core/elem.wast
+@@ -571,9 +571,11 @@
+   (func $const-i32-d (type $out-i32) (i32.const 68))
+ )
+ 
++(;
+ (assert_return (invoke $module1 "call-7") (i32.const 67))
+ (assert_return (invoke $module1 "call-8") (i32.const 68))
+ (assert_return (invoke $module1 "call-9") (i32.const 66))
++;)
+ 
+ (module $module3
+   (type $out-i32 (func (result i32)))
+@@ -584,6 +586,8 @@
+   (func $const-i32-f (type $out-i32) (i32.const 70))
+ )
+ 
++(;
+ (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))
++;)
+diff --git a/test/core/imports.wast b/test/core/imports.wast
+index 94c1af5c..bb1704fc 100644
+--- a/test/core/imports.wast
++++ b/test/core/imports.wast
+@@ -86,7 +86,7 @@
+ (assert_return (invoke "print64" (i64.const 24)))
+ 
+ (assert_invalid
+-  (module 
++  (module
+     (type (func (result i32)))
+     (import "test" "func" (func (type 1)))
+   )
+@@ -559,6 +559,7 @@
+ (assert_return (invoke "grow" (i32.const 1)) (i32.const -1))
+ (assert_return (invoke "grow" (i32.const 0)) (i32.const 2))
+ 
++(;
+ (module $Mgm
+   (memory (export "memory") 1) ;; initial size is 1
+   (func (export "grow") (result i32) (memory.grow (i32.const 1)))
+@@ -567,7 +568,7 @@
+ (assert_return (invoke $Mgm "grow") (i32.const 1)) ;; now size is 2
+ (module $Mgim1
+   ;; imported memory limits should match, because external memory size is 2 now
+-  (memory (export "memory") (import "grown-memory" "memory") 2) 
++  (memory (export "memory") (import "grown-memory" "memory") 2)
+   (func (export "grow") (result i32) (memory.grow (i32.const 1)))
+ )
+ (register "grown-imported-memory" $Mgim1)
+@@ -578,7 +579,7 @@
+   (func (export "size") (result i32) (memory.size))
+ )
+ (assert_return (invoke $Mgim2 "size") (i32.const 3))
+-
++;)
+ 
+ ;; Syntax errors
+ 
+@@ -650,6 +651,7 @@
+   "import after memory"
+ )
+ 
++(;
+ ;; This module is required to validate, regardless of whether it can be
+ ;; linked. Overloading is not possible in wasm itself, but it is possible
+ ;; in modules from which wasm can import.
+@@ -676,3 +678,4 @@
+   )
+   "unknown import"
+ )
++;)
+\ No newline at end of file
+diff --git a/test/core/linking.wast b/test/core/linking.wast
+index 994e0f49..8fbcc021 100644
+--- a/test/core/linking.wast
++++ b/test/core/linking.wast
+@@ -19,11 +19,11 @@
+ (assert_return (invoke $Nf "call") (i32.const 3))
+ (assert_return (invoke $Nf "call Mf.call") (i32.const 2))
+ 
+-(module
++(module $M1
+   (import "spectest" "print_i32" (func $f (param i32)))
+   (export "print" (func $f))
+ )
+-(register "reexport_f")
++(register "reexport_f" $M1)
+ (assert_unlinkable
+   (module (import "reexport_f" "print" (func (param i64))))
+   "incompatible import type"
+@@ -35,7 +35,6 @@
+ 
+ 
+ ;; Globals
+-
+ (module $Mg
+   (global $glob (export "glob") i32 (i32.const 42))
+   (func (export "get") (result i32) (global.get $glob))
+@@ -47,6 +46,7 @@
+ )
+ (register "Mg" $Mg)
+ 
++(; only sharing initial values
+ (module $Ng
+   (global $x (import "Mg" "glob") i32)
+   (global $mut_glob (import "Mg" "mut_glob") (mut i32))
+@@ -81,7 +81,7 @@
+ (assert_return (get $Ng "Mg.mut_glob") (i32.const 241))
+ (assert_return (invoke $Mg "get_mut") (i32.const 241))
+ (assert_return (invoke $Ng "Mg.get_mut") (i32.const 241))
+-
++;)
+ 
+ (assert_unlinkable
+   (module (import "Mg" "mut_glob" (global i32)))
+@@ -130,7 +130,7 @@
+ 
+ 
+ ;; Tables
+-
++(; no such support
+ (module $Mt
+   (type (func (result i32)))
+   (type (func))
+@@ -307,10 +307,11 @@
+   (module (table (import "Mtable_ex" "t-extern") 1 funcref))
+   "incompatible import type"
+ )
++;)
+ 
+ 
+ ;; Memories
+-
++(; no such support
+ (module $Mm
+   (memory (export "mem") 1 5)
+   (data (i32.const 10) "\00\01\02\03\04\05\06\07\08\09")
+@@ -451,3 +452,4 @@
+ 
+ (assert_return (invoke $Ms "get memory[0]") (i32.const 104))  ;; 'h'
+ (assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
++;)
+\ No newline at end of file
+diff --git a/test/core/load.wast b/test/core/load.wast
+index 9fe48e2b..3e9c2f8c 100644
+--- a/test/core/load.wast
++++ b/test/core/load.wast
+@@ -29,6 +29,8 @@
+ (register "M")
+ 
+ (module
++  (func $readM1 (import "M" "read") (param i32) (result i32))
++  (export "readM1" (func $readM1))
+   (memory $mem1 (import "M" "mem") 2)
+   (memory $mem2 3)
+ 
+@@ -43,11 +45,12 @@
+   )
+ )
+ 
+-(assert_return (invoke $M "read" (i32.const 20)) (i32.const 1))
+-(assert_return (invoke $M "read" (i32.const 21)) (i32.const 2))
+-(assert_return (invoke $M "read" (i32.const 22)) (i32.const 3))
+-(assert_return (invoke $M "read" (i32.const 23)) (i32.const 4))
+-(assert_return (invoke $M "read" (i32.const 24)) (i32.const 5))
++;; To invoke the function in M as a submodule, not as an independent module
++(assert_return (invoke "readM1" (i32.const 20)) (i32.const 1))
++(assert_return (invoke "readM1" (i32.const 21)) (i32.const 2))
++(assert_return (invoke "readM1" (i32.const 22)) (i32.const 3))
++(assert_return (invoke "readM1" (i32.const 23)) (i32.const 4))
++(assert_return (invoke "readM1" (i32.const 24)) (i32.const 5))
+ 
+ (assert_return (invoke "read1" (i32.const 20)) (i32.const 1))
+ (assert_return (invoke "read1" (i32.const 21)) (i32.const 2))
+diff --git a/test/core/memory_grow.wast b/test/core/memory_grow.wast
+index 4b6dbc83..dc46c029 100644
+--- a/test/core/memory_grow.wast
++++ b/test/core/memory_grow.wast
+@@ -106,15 +106,15 @@
+ 
+ ;; Multiple memories
+ 
+-(module
++(module $MemroygrowM
+   (memory (export "mem1") 2 5)
+   (memory (export "mem2") 0)
+ )
+-(register "M")
++(register "MemroygrowM" $MemorygrowM)
+ 
+ (module
+-  (memory $mem1 (import "M" "mem1") 1 6)
+-  (memory $mem2 (import "M" "mem2") 0)
++  (memory $mem1 (import "MemroygrowM" "mem1") 1 6)
++  (memory $mem2 (import "MemroygrowM" "mem2") 0)
+   (memory $mem3 3)
+   (memory $mem4 4 5)
+ 
+diff --git a/test/core/memory_size.wast b/test/core/memory_size.wast
+index a1d6ea2d..b58c75d0 100644
+--- a/test/core/memory_size.wast
++++ b/test/core/memory_size.wast
+@@ -65,15 +65,15 @@
+ 
+ ;; Multiple memories
+ 
+-(module
++(module $MemmorysizeM
+   (memory (export "mem1") 2 4)
+   (memory (export "mem2") 0)
+ )
+-(register "M")
++(register "MemmorysizeM" $MemmorysizeM)
+ 
+ (module
+-  (memory $mem1 (import "M" "mem1") 1 5)
+-  (memory $mem2 (import "M" "mem2") 0)
++  (memory $mem1 (import "MemmorysizeM" "mem1") 1 5)
++  (memory $mem2 (import "MemmorysizeM" "mem2") 0)
+   (memory $mem3 3)
+   (memory $mem4 4 5)
+ 
+diff --git a/test/core/multi-memory/imports2.wast b/test/core/multi-memory/imports2.wast
+index 314bc131..e1060599 100644
+--- a/test/core/multi-memory/imports2.wast
++++ b/test/core/multi-memory/imports2.wast
+@@ -1,13 +1,13 @@
+-(module
++(module $imports2test
+   (memory (export "z") 0 0)
+   (memory (export "memory-2-inf") 2)
+   (memory (export "memory-2-4") 2 4)
+ )
+ 
+-(register "test")
++(register "imports2test" $imports2test)
+ 
+ (module
+-  (import "test" "z" (memory 0))
++  (import "imports2test" "z" (memory 0))
+   (memory $m (import "spectest" "memory") 1 2)
+   (data (memory 1) (i32.const 10) "\10")
+ 
+@@ -31,9 +31,9 @@
+ (assert_trap (invoke "load" (i32.const 1000000)) "out of bounds memory access")
+ 
+ (module
+-  (import "test" "memory-2-inf" (memory 2))
+-  (import "test" "memory-2-inf" (memory 1))
+-  (import "test" "memory-2-inf" (memory 0))
++  (import "imports2test" "memory-2-inf" (memory 2))
++  (import "imports2test" "memory-2-inf" (memory 1))
++  (import "imports2test" "memory-2-inf" (memory 0))
+ )
+ 
+ (module
+@@ -46,7 +46,7 @@
+ )
+ 
+ (assert_unlinkable
+-  (module (import "test" "unknown" (memory 1)))
++  (module (import "imports2test" "unknown" (memory 1)))
+   "unknown import"
+ )
+ (assert_unlinkable
+@@ -55,11 +55,11 @@
+ )
+ 
+ (assert_unlinkable
+-  (module (import "test" "memory-2-inf" (memory 3)))
++  (module (import "imports2test" "memory-2-inf" (memory 3)))
+   "incompatible import type"
+ )
+ (assert_unlinkable
+-  (module (import "test" "memory-2-inf" (memory 2 3)))
++  (module (import "imports2test" "memory-2-inf" (memory 2 3)))
+   "incompatible import type"
+ )
+ (assert_unlinkable
+diff --git a/test/core/multi-memory/imports4.wast b/test/core/multi-memory/imports4.wast
+index 411b1c0f..0a819454 100644
+--- a/test/core/multi-memory/imports4.wast
++++ b/test/core/multi-memory/imports4.wast
+@@ -1,12 +1,12 @@
+-(module
++(module $imports4test
+   (memory (export "memory-2-inf") 2)
+   (memory (export "memory-2-4") 2 4)
+ )
+ 
+-(register "test")
++(register "imports4test")
+ 
+ (module
+-  (import "test" "memory-2-4" (memory 1))
++  (import "imports4test" "memory-2-4" (memory 1))
+   (memory $m (import "spectest" "memory") 0 3)  ;; actual has max size 2
+   (func (export "grow") (param i32) (result i32) (memory.grow $m (local.get 0)))
+ )
+@@ -16,6 +16,8 @@
+ (assert_return (invoke "grow" (i32.const 1)) (i32.const -1))
+ (assert_return (invoke "grow" (i32.const 0)) (i32.const 2))
+ 
++;; TODO: Current implementation call grow on one submodule instance can't really change its definition
++(;
+ (module $Mgm
+   (memory 0)
+   (memory 0)
+@@ -45,3 +47,4 @@
+   (func (export "size") (result i32) (memory.size $m))
+ )
+ (assert_return (invoke $Mgim2 "size") (i32.const 3))
++;)
+\ No newline at end of file
+diff --git a/test/core/multi-memory/linking0.wast b/test/core/multi-memory/linking0.wast
+index b09c69f6..d57d484e 100644
+--- a/test/core/multi-memory/linking0.wast
++++ b/test/core/multi-memory/linking0.wast
+@@ -24,8 +24,8 @@
+   )
+   "unknown import"
+ )
+-(assert_trap (invoke $Mt "call" (i32.const 7)) "uninitialized element")
+-
++;; can't call function in submodule when module can't be instantiated
++;; (assert_trap (invoke "call" (i32.const 7)) "uninitialized element")
+ 
+ (assert_trap
+   (module
+@@ -39,4 +39,5 @@
+   )
+   "out of bounds memory access"
+ )
+-(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
++;; can't call function in submodule when module can't be instantiated
++;; (assert_return (invoke "call" (i32.const 7)) (i32.const 0))
+diff --git a/test/core/multi-memory/linking1.wast b/test/core/multi-memory/linking1.wast
+index 39eabb00..49c87ce8 100644
+--- a/test/core/multi-memory/linking1.wast
++++ b/test/core/multi-memory/linking1.wast
+@@ -1,4 +1,4 @@
+-(module $Mm
++(module $linking1Mm
+   (memory $mem0 (export "mem0") 0 0)
+   (memory $mem1 (export "mem1") 1 5)
+   (memory $mem2 (export "mem2") 0 0)
+@@ -9,11 +9,11 @@
+     (i32.load8_u $mem1 (local.get 0))
+   )
+ )
+-(register "Mm" $Mm)
++(register "linking1Mm" $linking1Mm)
+ 
+-(module $Nm
+-  (func $loadM (import "Mm" "load") (param i32) (result i32))
+-  (memory (import "Mm" "mem0") 0)
++(module $linking1Nm
++  (func $loadM (import "linking1Mm" "load") (param i32) (result i32))
++  (memory (import "linking1Mm" "mem0") 0)
+ 
+   (memory $m 1)
+   (data (memory 1) (i32.const 10) "\f0\f1\f2\f3\f4\f5")
+@@ -24,12 +24,14 @@
+   )
+ )
+ 
+-(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 2))
+-(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 2))
+-(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2))
++(assert_return (invoke $linking1Mm "load" (i32.const 12)) (i32.const 2))
++(assert_return (invoke $linking1Nm "Mm.load" (i32.const 12)) (i32.const 2))
++(assert_return (invoke $linking1Nm "load" (i32.const 12)) (i32.const 0xf2))
+ 
+-(module $Om
+-  (memory (import "Mm" "mem1") 1)
++(module $linking1Om
++  (func $loadM (import "linking1Mm" "load") (param i32) (result i32))
++  (export "Mm.load" (func $loadM))
++  (memory (import "linking1Mm" "mem1") 1)
+   (data (i32.const 5) "\a0\a1\a2\a3\a4\a5\a6\a7")
+ 
+   (func (export "load") (param $a i32) (result i32)
+@@ -37,19 +39,20 @@
+   )
+ )
+ 
+-(assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 0xa7))
+-(assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7))
+-(assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2))
+-(assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7))
++;; To invoke the function in Mm as a submodule, not as an independent module
++(assert_return (invoke $linking1Om "Mm.load" (i32.const 12)) (i32.const 0xa7))
++;; (assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7))
++;; (assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2))
++(assert_return (invoke $linking1Om "load" (i32.const 12)) (i32.const 0xa7))
+ 
+ (module
+-  (memory (import "Mm" "mem1") 0)
++  (memory (import "linking1Mm" "mem1") 0)
+   (data (i32.const 0xffff) "a")
+ )
+ 
+ (assert_trap
+   (module
+-    (memory (import "Mm" "mem0") 0)
++    (memory (import "linking1Mm" "mem0") 0)
+     (data (i32.const 0xffff) "a")
+   )
+   "out of bounds memory access"
+@@ -57,7 +60,7 @@
+ 
+ (assert_trap
+   (module
+-    (memory (import "Mm" "mem1") 0)
++    (memory (import "linking1Mm" "mem1") 0)
+     (data (i32.const 0x10000) "a")
+   )
+   "out of bounds memory access"
+diff --git a/test/core/multi-memory/linking2.wast b/test/core/multi-memory/linking2.wast
+index 26bf3cca..5eae4643 100644
+--- a/test/core/multi-memory/linking2.wast
++++ b/test/core/multi-memory/linking2.wast
+@@ -1,4 +1,4 @@
+-(module $Mm
++(module $linking2Mm
+   (memory $mem0 (export "mem0") 0 0)
+   (memory $mem1 (export "mem1") 1 5)
+   (memory $mem2 (export "mem2") 0 0)
+@@ -9,22 +9,22 @@
+     (i32.load8_u $mem1 (local.get 0))
+   )
+ )
+-(register "Mm" $Mm)
++(register "linking2Mm" $linking2Mm)
+ 
+-(module $Pm
+-  (memory (import "Mm" "mem1") 1 8)
++(module
++  (memory (import "linking2Mm" "mem1") 1 8)
+ 
+   (func (export "grow") (param $a i32) (result i32)
+     (memory.grow (local.get 0))
+   )
+ )
+ 
+-(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1))
+-(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1))
+-(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3))
+-(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3))
+-(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4))
+-(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
+-(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1))
+-(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
++(assert_return (invoke "grow" (i32.const 0)) (i32.const 1))
++(assert_return (invoke "grow" (i32.const 2)) (i32.const 1))
++(assert_return (invoke "grow" (i32.const 0)) (i32.const 3))
++(assert_return (invoke "grow" (i32.const 1)) (i32.const 3))
++(assert_return (invoke "grow" (i32.const 1)) (i32.const 4))
++(assert_return (invoke "grow" (i32.const 0)) (i32.const 5))
++(assert_return (invoke "grow" (i32.const 1)) (i32.const -1))
++(assert_return (invoke "grow" (i32.const 0)) (i32.const 5))
+ 
+diff --git a/test/core/multi-memory/linking3.wast b/test/core/multi-memory/linking3.wast
+index e23fbe4e..d3efe95a 100644
+--- a/test/core/multi-memory/linking3.wast
++++ b/test/core/multi-memory/linking3.wast
+@@ -33,8 +33,9 @@
+   )
+   "out of bounds memory access"
+ )
+-(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97))
+-(assert_return (invoke $Mm "load" (i32.const 327670)) (i32.const 0))
++;; can't call function in submodule when module can't be instantiated
++;; (assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97))
++;; (assert_return (invoke $Mm "load" (i32.const 327670)) (i32.const 0))
+ 
+ (assert_trap
+   (module
+@@ -46,7 +47,8 @@
+   )
+   "out of bounds table access"
+ )
+-(assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97))
++;; can't call function in submodule when module can't be instantiated
++;; (assert_return (invoke $Mm "load" (i32.const 0)) (i32.const 97))
+ 
+ ;; Store is modified if the start function traps.
+ (module $Ms
+@@ -79,5 +81,6 @@
+   "unreachable"
+ )
+ 
+-(assert_return (invoke $Ms "get memory[0]") (i32.const 104))  ;; 'h'
+-(assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
++;; can't call function in submodule when module can't be instantiated
++;; (assert_return (invoke $Ms "get memory[0]") (i32.const 104))  ;; 'h'
++;; (assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
+diff --git a/test/core/multi-memory/load1.wast b/test/core/multi-memory/load1.wast
+index be309c39..6a0faf0d 100644
+--- a/test/core/multi-memory/load1.wast
++++ b/test/core/multi-memory/load1.wast
+@@ -8,6 +8,8 @@
+ (register "M")
+ 
+ (module
++  (func $readM1 (import "M" "read") (param i32) (result i32))
++  (export "readM1" (func $readM1))
+   (memory $mem1 (import "M" "mem") 2)
+   (memory $mem2 3)
+ 
+@@ -22,11 +24,12 @@
+   )
+ )
+ 
+-(assert_return (invoke $M "read" (i32.const 20)) (i32.const 1))
+-(assert_return (invoke $M "read" (i32.const 21)) (i32.const 2))
+-(assert_return (invoke $M "read" (i32.const 22)) (i32.const 3))
+-(assert_return (invoke $M "read" (i32.const 23)) (i32.const 4))
+-(assert_return (invoke $M "read" (i32.const 24)) (i32.const 5))
++;; To invoke the function in M as a submodule, not as an independent module
++(assert_return (invoke "readM1" (i32.const 20)) (i32.const 1))
++(assert_return (invoke "readM1" (i32.const 21)) (i32.const 2))
++(assert_return (invoke "readM1" (i32.const 22)) (i32.const 3))
++(assert_return (invoke "readM1" (i32.const 23)) (i32.const 4))
++(assert_return (invoke "readM1" (i32.const 24)) (i32.const 5))
+ 
+ (assert_return (invoke "read1" (i32.const 20)) (i32.const 1))
+ (assert_return (invoke "read1" (i32.const 21)) (i32.const 2))
+diff --git a/test/core/multi-memory/store1.wast b/test/core/multi-memory/store1.wast
+index 10cf2c42..eafe6cc9 100644
+--- a/test/core/multi-memory/store1.wast
++++ b/test/core/multi-memory/store1.wast
+@@ -10,6 +10,9 @@
+ )
+ (register "M1")
+ 
++(invoke "store" (i32.const 0) (i64.const 1))
++(assert_return (invoke "load" (i32.const 0)) (i64.const 1))
++
+ (module $M2
+   (memory (export "mem") 1)
+ 
+@@ -22,10 +25,8 @@
+ )
+ (register "M2")
+ 
+-(invoke $M1 "store" (i32.const 0) (i64.const 1))
+-(invoke $M2 "store" (i32.const 0) (i64.const 2))
+-(assert_return (invoke $M1 "load" (i32.const 0)) (i64.const 1))
+-(assert_return (invoke $M2 "load" (i32.const 0)) (i64.const 2))
++(invoke "store" (i32.const 0) (i64.const 2))
++(assert_return (invoke "load" (i32.const 0)) (i64.const 2))
+ 
+ (module
+   (memory $mem1 (import "M1" "mem") 1)
+diff --git a/test/core/ref_func.wast b/test/core/ref_func.wast
+index adb5cb78..6396013b 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/store.wast b/test/core/store.wast
+index 86f6263a..65a0d4ee 100644
+--- a/test/core/store.wast
++++ b/test/core/store.wast
+@@ -35,7 +35,10 @@
+     (i64.store (local.get 0) (local.get 1))
+   )
+ )
+-(register "M1")
++(register "M1" $M1)
++
++(invoke "store" (i32.const 0) (i64.const 1))
++(assert_return (invoke "load" (i32.const 0)) (i64.const 1))
+ 
+ (module $M2
+   (memory (export "mem") 1)
+@@ -47,12 +50,10 @@
+     (i64.store (local.get 0) (local.get 1))
+   )
+ )
+-(register "M2")
++(register "M2" $M2)
+ 
+-(invoke $M1 "store" (i32.const 0) (i64.const 1))
+-(invoke $M2 "store" (i32.const 0) (i64.const 2))
+-(assert_return (invoke $M1 "load" (i32.const 0)) (i64.const 1))
+-(assert_return (invoke $M2 "load" (i32.const 0)) (i64.const 2))
++(invoke "store" (i32.const 0) (i64.const 2))
++(assert_return (invoke "load" (i32.const 0)) (i64.const 2))
+ 
+ (module
+   (memory $mem1 (import "M1" "mem") 1)
+diff --git a/test/core/table_copy.wast b/test/core/table_copy.wast
+index 380e84ee..59230cfb 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 0b2d26f7..3c595e5b 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)

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

@@ -327,6 +327,9 @@ parser.add_argument('--gc', default=False, action='store_true',
 parser.add_argument('--memory64', default=False, action='store_true',
         help='Test with Memory64')
 
+parser.add_argument('--multi-memory', default=False, action='store_true',
+        help='Test with multi-memory(with multi-module auto enabled)')
+
 parser.add_argument('--qemu', default=False, action='store_true',
         help="Enable QEMU")
 
@@ -1097,6 +1100,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
         cmd = [opts.wast2wasm, "--enable-threads", "--no-check", "--enable-exceptions", "--enable-tail-call", wast_tempfile, "-o", wasm_tempfile ]
     elif opts.memory64:
         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 ]
     else:
         cmd = [opts.wast2wasm, "--enable-threads", "--no-check",
                wast_tempfile, "-o", wasm_tempfile ]

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

@@ -25,6 +25,7 @@ function help()
     echo "-S enable SIMD feature"
     echo "-G enable GC feature"
     echo "-W enable memory64 feature"
+    echo "-E enable multi memory feature"
     echo "-X enable XIP feature"
     echo "-e enable exception handling"
     echo "-x test SGX"
@@ -59,6 +60,7 @@ COLLECT_CODE_COVERAGE=0
 ENABLE_SIMD=0
 ENABLE_GC=0
 ENABLE_MEMORY64=0
+ENABLE_MULTI_MEMORY=0
 ENABLE_XIP=0
 ENABLE_EH=0
 ENABLE_DEBUG_VERSION=0
@@ -85,7 +87,7 @@ REQUIREMENT_NAME=""
 # Initialize an empty array for subrequirement IDs
 SUBREQUIREMENT_IDS=()
 
-while getopts ":s:cabgvt:m:MCpSXexwWPGQF:j:T:r:A:" opt
+while getopts ":s:cabgvt:m:MCpSXexwWEPGQF:j:T:r:A:" opt
 do
     OPT_PARSED="TRUE"
     case $opt in
@@ -148,6 +150,11 @@ do
         echo "enable wasm64(memory64) feature"
         ENABLE_MEMORY64=1
         ;;
+        E)
+        echo "enable multi memory feature(auto enable multi module)"
+        ENABLE_MULTI_MEMORY=1
+        ENABLE_MULTI_MODULE=1
+        ;;
         C)
         echo "enable code coverage"
         COLLECT_CODE_COVERAGE=1
@@ -496,6 +503,20 @@ function spec_test()
         git reset --hard 48e69f394869c55b7bbe14ac963c09f4605490b6
         git checkout 044d0d2e77bdcbe891f7e0b9dd2ac01d56435f0b -- test/core/elem.wast test/core/data.wast
         git apply ../../spec-test-script/memory64_ignore_cases.patch || exit 1
+    elif [[ ${ENABLE_MULTI_MEMORY} == 1 ]]; then
+        echo "checkout spec for multi memory proposal"
+
+        # check spec test cases for multi memory
+        git clone -b main --single-branch https://github.com/WebAssembly/multi-memory.git spec
+        pushd spec
+
+        # Reset to commit: "Merge pull request #48 from backes/specify-memcpy-immediate-order"
+        git reset --hard 48e69f394869c55b7bbe14ac963c09f4605490b6
+        git checkout 044d0d2e77bdcbe891f7e0b9dd2ac01d56435f0b -- test/core/elem.wast
+        git apply ../../spec-test-script/multi_memory_ignore_cases.patch || exit 1
+        if [[ ${RUNNING_MODE} == "aot" ]]; then
+            git apply ../../spec-test-script/multi_module_aot_ignore_cases.patch || exit 1
+        fi
     else
         echo "checkout spec for default proposal"
 
@@ -572,6 +593,13 @@ function spec_test()
         ARGS_FOR_SPEC_TEST+="--memory64 "
     fi
 
+    # multi memory is only enabled in interp and aot mode
+    if [[ 1 == ${ENABLE_MULTI_MEMORY} ]]; then
+        if [[ $1 == 'classic-interp' || $1 == 'aot' ]]; then
+            ARGS_FOR_SPEC_TEST+="--multi-memory "
+        fi
+    fi
+
     if [[ ${ENABLE_QEMU} == 1 ]]; then
         ARGS_FOR_SPEC_TEST+="--qemu "
         ARGS_FOR_SPEC_TEST+="--qemu-firmware ${QEMU_FIRMWARE} "
@@ -852,6 +880,14 @@ function do_execute_in_running_mode()
 {
     local RUNNING_MODE="$1"
 
+    if [[ ${ENABLE_MULTI_MEMORY} -eq 1 ]]; then
+        if [[ "${RUNNING_MODE}" != "classic-interp" \
+                && "${RUNNING_MODE}" != "aot" ]]; then
+            echo "support multi-memory in classic-interp mode and aot mode"
+            return 0
+        fi
+    fi
+
     if [[ ${ENABLE_MEMORY64} -eq 1 ]]; then
         if [[ "${RUNNING_MODE}" != "classic-interp" \
                 && "${RUNNING_MODE}" != "aot" ]]; then
@@ -941,6 +977,12 @@ function trigger()
         EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_MEMORY64=0"
     fi
 
+    if [[ ${ENABLE_MULTI_MEMORY} == 1 ]];then
+        EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_MULTI_MEMORY=1"
+    else
+        EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_MULTI_MEMORY=0"
+    fi
+
     if [[ ${ENABLE_MULTI_THREAD} == 1 ]];then
         EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_LIB_PTHREAD=1"
     fi

+ 2 - 2
wamr-compiler/main.c

@@ -601,8 +601,8 @@ main(int argc, char *argv[])
             LOG_VERBOSE("Set size level to 1 for Windows AOT file");
             option.size_level = 1;
         }
-#if defined(_WIN32) || defined(_WIN32_) || defined(__APPLE__) \
-    || defined(__MACH__)
+#if defined(_WIN32) || defined(_WIN32_) \
+    || ((defined(__APPLE__) || defined(__MACH__)) && !defined(__arm64__))
         if (!option.target_arch && !option.target_abi) {
             LOG_VERBOSE("Set size level to 1 for Windows or MacOS AOT file");
             option.size_level = 1;