Quellcode durchsuchen

Merge pull request #3740 from bytecodealliance/main

Merge branch main into dev/dynamic_linking
Wenyong Huang vor 1 Jahr
Ursprung
Commit
cfc6f4ba55
34 geänderte Dateien mit 2066 neuen und 420 gelöschten Zeilen
  1. 3 3
      .github/workflows/build_wamr_vscode_ext.yml
  2. 36 8
      .github/workflows/compilation_on_android_ubuntu.yml
  3. 24 19
      .github/workflows/compilation_on_nuttx.yml
  4. 23 3
      .github/workflows/nightly_run.yml
  5. 0 4
      .github/workflows/spec_test_on_nuttx.yml
  6. 74 0
      RELEASE_NOTES.md
  7. 5 0
      build-scripts/config_common.cmake
  8. 5 0
      core/config.h
  9. 171 72
      core/iwasm/aot/aot_loader.c
  10. 7 0
      core/iwasm/aot/aot_runtime.c
  11. 11 0
      core/iwasm/aot/aot_runtime.h
  12. 2 0
      core/iwasm/aot/arch/aot_reloc_riscv.c
  13. 36 2
      core/iwasm/common/wasm_memory.c
  14. 56 35
      core/iwasm/common/wasm_runtime_common.c
  15. 1 0
      core/iwasm/compilation/aot.c
  16. 28 32
      core/iwasm/compilation/aot_emit_const.c
  17. 1 0
      core/iwasm/compilation/aot_emit_memory.c
  18. 21 2
      core/iwasm/compilation/aot_llvm.c
  19. 6 0
      core/iwasm/compilation/aot_llvm_extra.cpp
  20. 1 0
      core/iwasm/fast-jit/fe/jit_emit_memory.c
  21. 8 0
      core/iwasm/interpreter/wasm.h
  22. 112 51
      core/iwasm/interpreter/wasm_interp_classic.c
  23. 1 0
      core/iwasm/interpreter/wasm_interp_fast.c
  24. 84 39
      core/iwasm/interpreter/wasm_loader.c
  25. 72 36
      core/iwasm/interpreter/wasm_mini_loader.c
  26. 159 100
      core/iwasm/interpreter/wasm_runtime.c
  27. 7 0
      core/iwasm/interpreter/wasm_runtime.h
  28. 3 2
      core/shared/platform/include/platform_api_extension.h
  29. 1 0
      core/shared/utils/bh_atomic.h
  30. 1 1
      core/version.h
  31. 24 2
      tests/wamr-test-suites/spec-test-script/all.py
  32. 1022 0
      tests/wamr-test-suites/spec-test-script/multi_memory_ignore_cases.patch
  33. 18 8
      tests/wamr-test-suites/spec-test-script/runtest.py
  34. 43 1
      tests/wamr-test-suites/test_wamr.sh

+ 3 - 3
.github/workflows/build_wamr_vscode_ext.yml

@@ -20,10 +20,10 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
-      - name: Use Node.js 16.x
+      - name: Use Node.js 18.x
         uses: actions/setup-node@v4
         with:
-          node-version: 16.x
+          node-version: 18.x
 
       - name: set vscode extension to correct version
         run: |
@@ -33,7 +33,7 @@ jobs:
 
       - name: generate wamr ide vscode extension
         run: |
-          npm install -g vsce
+          npm install -g @vscode/vsce
           rm -rf node_modules
           npm install
           vsce package

+ 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 }}

+ 24 - 19
.github/workflows/compilation_on_nuttx.yml

@@ -68,18 +68,17 @@ jobs:
           # arm64
           "boards/arm64/qemu/qemu-armv8a/configs/nsh",
         ]
-        wamr_config_option: [
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_WASI=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_WASI=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\nCONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_AOT=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_FAST=y\\n",
-          "CONFIG_INTERPRETERS_WAMR=y\\nCONFIG_INTERPRETERS_WAMR_CLASSIC=y\\n",
-        ]
+
+        wamr_config_option:
+          - "CONFIG_INTERPRETERS_WAMR_AOT"
+          - "CONFIG_INTERPRETERS_WAMR_FAST"
+          - "CONFIG_INTERPRETERS_WAMR_CLASSIC"
+          - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_FAST"
+          - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_FAST CONFIG_INTERPRETERS_WAMR_LIBC_WASI"
+          - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_FAST CONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN"
+          - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_CLASSIC"
+          - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_CLASSIC CONFIG_INTERPRETERS_WAMR_LIBC_WASI"
+          - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_CLASSIC CONFIG_INTERPRETERS_WAMR_LIBC_WASI"
 
     steps:
       - name: Checkout NuttX
@@ -102,16 +101,22 @@ jobs:
           repository: ${{ github.repository }}
           path: apps/interpreters/wamr/wamr
 
-      - name: Enable WAMR for NuttX
+      - name: Configure WAMR
+        working-directory: nuttx
         run: |
-          find nuttx/boards -name defconfig | xargs sed -i '$a\CONFIG_EOL_IS_LF=y\n${{ matrix.wamr_config_option }}'
-          find nuttx/boards/sim -name defconfig | xargs sed -i '$a\CONFIG_LIBM=y\n'
+          tools/configure.sh ${{ matrix.nuttx_board_config }}
+          kconfig-tweak --enable CONFIG_PSEUDOFS_SOFTLINKS
+          kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR
+          kconfig-tweak --enable CONFIG_INTERPRETERS_IWASM_TASK
+          kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_PRIORITY 100
+          kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_STACKSIZE 8192
+          for x in ${{ matrix.wamr_config_option }}; do
+            kconfig-tweak --enable $x
+          done
 
       - name: Build
-        run: |
-          cd nuttx
-          tools/configure.sh ${{ matrix.nuttx_board_config }}
-          make -j$(nproc) EXTRAFLAGS=-Werror
+        working-directory: nuttx
+        run: make -j$(nproc) EXTRAFLAGS=-Werror
 
       - name: Checkout Bloaty
         uses: actions/checkout@v3

+ 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: |

+ 0 - 4
.github/workflows/spec_test_on_nuttx.yml

@@ -125,10 +125,6 @@ jobs:
           - target_config: { config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh64" }
             wamr_test_option: { mode: "-t aot -X" }
 
-          # XIP is not fully supported yet on RISCV32 ILP32F, some relocations can not be resolved
-          - target_config: { config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh", fpu_type: "fp" }
-            wamr_test_option:  { mode: "-t aot -X" }
-
           # Our xtensa environment doesn't have enough memory
           - target_config: { target: "xtensa" }
             wamr_feature_option: { mode: "-G" }

+ 74 - 0
RELEASE_NOTES.md

@@ -1,3 +1,77 @@
+## WAMR-2.1.2
+
+### Breaking Changes
+ - wasi-nn: Apply new architecture (#3692)
+
+### New Features
+ - [wasi-nn] Add a new wasi-nn backend openvino (#3603)
+ - Add APIs into wasm_c_api.h to summary wasm function execution duration (#3639)
+ - Add support for RISCV32 ILP32F (#3708)
+
+### Bug Fixes
+ - libc-builtin: Fix function prototype for wasm_runtime_module_realloc (#3702)
+ - Fix potential memory leak in insert_native_symbol (#3712)
+ - aot compiler: Fix NaN handling for opcode f32/f64.const in XIP mode (#3721)
+ - Fix table idx resolving in op call_indirect/return_call_indirect (#3726)
+
+### Enhancements
+ - Remove a few hardcoded spec test knowledge from the core library (#3648)
+ - Change log of import function to be consistent (#3656)
+ - libc-builtin: Fix a printf format (#3652)
+ - Set compile symbol visibility to hidden in cmake (#3655)
+ - wamrc: Add --mllvm= option (#3658)
+ - wamr-compiler: Avoid size-level tweak if target is specified (#3659)
+ - aot runtime: Add missing arm/thumb relocations (#3660)
+ - aot compiler: Enlarge AOTNativeSymbol->symbol (#3662)
+ - aot compiler: Bail out on too long native symbol names (#3663)
+ - Support more features for rt-thread (#3661)
+ - Zephyr User Mode Support (#3650)
+ - Set posix thread name for debug build (#3657)
+ - Add emscripten_sleep() wrapper to libc-emcc (#3669)
+ - Fix a compilation warning (#3682)
+ - wamrc: Add some help text for --size-level (#3689)
+ - Restore linux iwasm default visibility (#3691)
+ - posix_thread.c: Restore old signal alternate stack before thread exit (#3693)
+ - libc-wasi: Make rights of STDIN/STDOUT/STDERR fixed and overlook their access modes (#3694)
+ - [refactoring] Extract read leb to a separate file, share the code between loader and mini loader (#3701)
+ - debug-interp: Only add lock when signal_flag is SIG_SINGSTEP (#3704)
+ - Fix compilation warnings (#3707)
+ - Add missing headers in bh_atomic.h and aot_llvm_extra.cpp (#3715)
+ - Update std atomic check and simd compatibility check for arc compiler (#3716)
+ - aot compiler: Track non-0x00 tableindex as ref types use (#3695)
+ - compilation: Use the dedicated stack-sizes section only for AOT (#3732)
+ - riscv: Add missing relocation intrinsics for __fixdfsi/__ltdf2 (#3733)
+
+### Others
+ - Fix night run CI (#3640)
+ - spec-test-script/runtest.py: Don't assume the tmp dir path (#3632)
+ - wamr-test-suites: Remove dead code (wasi_test) (#3634)
+ - wamr-test-suites/test_wamr.sh: Add an option to specify wamrc binary (#3635)
+ - CI: Build llvm for xtensa (#3637)
+ - spec-test-script/runtest.py: Avoid specifying -v=0 unnecessarily (#3642)
+ - spec-test-script: Add xtensa case (#3643)
+ - spec-test-script/runtest.py: Move "--size-level=1" to common place for RISCV64 (#3644)
+ - spec-test-script/runtest.py: Use a shorter timeout when expected to fail (#3647)
+ - spec-test-script: Make case_last_words larger (#3651)
+ - spec-test-script/runtest.py: Reduce stack size for aot w/o gc (#3653)
+ - spec-test-script: Skip a few tests for xtensa qemu (#3664)
+ - spec-test-script: Use -mtext-section-literals for xtensa xip (#3666)
+ - spec_test_on_nuttx.yml: Add xtensa (#3665)
+ - spec_test_on_nuttx.yml: Enable xip (#3671)
+ - spec_test_on_nuttx.yml: Record more logs (#3670)
+ - spec_test_on_nuttx.yml: Replace sed with kconfig-tweak (#3672)
+ - spec_test_on_nuttx.yml: Retire CONFIG_EOL_IS_LF (#3676)
+ - spec-test-script/runtest.py: Use wamrc --xip option for xip (#3683)
+ - CI: Bump NuttX version to 12.6 (#3684)
+ - wamr-test-suites: Clean up generated tmp files after spec test (#3700)
+ - test_wamr.sh: Fix build wabt tool (#3703)
+ - NuttX: Retire CONFIG_ARCH_RV32IM and CONFIG_ARCH_RV64GC (#3717)
+ - runtest.py: Normallize option handling for XIP mode (#3722)
+ - CI: Enable XIP spectest for RISCV32 ILP32F (#3727)
+ - CI: Unify configuration stage for NuttX (#3725)
+
+---
+
 ## WAMR-2.1.1
 
 ### Breaking Changes

+ 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

+ 171 - 72
core/iwasm/aot/aot_loader.c

@@ -294,6 +294,39 @@ loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size)
     return mem;
 }
 
+static void *
+loader_mmap(uint32 size, bool prot_exec, char *error_buf, uint32 error_buf_size)
+{
+    int map_prot =
+        MMAP_PROT_READ | MMAP_PROT_WRITE | (prot_exec ? MMAP_PROT_EXEC : 0);
+    int map_flags;
+    void *mem;
+
+#if UINTPTR_MAX == UINT64_MAX
+    /* The mmapped AOT data and code in 64-bit targets had better be in
+       range 0 to 2G, or aot loader may fail to apply some relocations,
+       e.g., R_X86_64_32/R_X86_64_32S/R_X86_64_PC32/R_RISCV_32.
+       We try to mmap with MMAP_MAP_32BIT flag first, and if fails, mmap
+       again without the flag. */
+    map_flags = MMAP_MAP_32BIT;
+    if ((mem = os_mmap(NULL, size, map_prot, map_flags,
+                       os_get_invalid_handle()))) {
+        /* The mmapped memory must be in the first 2 Gigabytes of the
+           process address space */
+        bh_assert((uintptr_t)mem < INT32_MAX);
+        return mem;
+    }
+#endif
+
+    map_flags = MMAP_MAP_NONE;
+    if (!(mem = os_mmap(NULL, size, map_prot, map_flags,
+                        os_get_invalid_handle()))) {
+        set_error_buf(error_buf, error_buf_size, "allocate memory failed");
+        return NULL;
+    }
+    return mem;
+}
+
 static char *
 load_string(uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
             bool is_load_from_file_buf,
@@ -2378,7 +2411,6 @@ destroy_object_data_sections(AOTObjectDataSection *data_sections,
                 }
             }
 #endif
-            os_munmap(data_section->data, data_section->size);
         }
     wasm_runtime_free(data_sections);
 }
@@ -2392,6 +2424,9 @@ load_object_data_sections(const uint8 **p_buf, const uint8 *buf_end,
     AOTObjectDataSection *data_sections;
     uint64 size;
     uint32 i;
+    uint64 total_size = 0;
+    uint32 page_size = os_getpagesize();
+    uint8 *merged_sections = NULL;
 
     /* Allocate memory */
     size = sizeof(AOTObjectDataSection) * (uint64)module->data_section_count;
@@ -2400,41 +2435,40 @@ load_object_data_sections(const uint8 **p_buf, const uint8 *buf_end,
         return false;
     }
 
-    /* Create each data section */
+    /* First iteration: read data from buf, and calculate total memory needed */
     for (i = 0; i < module->data_section_count; i++) {
-        int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE;
-#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \
-    || defined(BUILD_TARGET_RISCV64_LP64D)                       \
-    || defined(BUILD_TARGET_RISCV64_LP64)
-        /* aot code and data in x86_64 must be in range 0 to 2G due to
-           relocation for R_X86_64_32/32S/PC32 */
-        int map_flags = MMAP_MAP_32BIT;
-#else
-        int map_flags = MMAP_MAP_NONE;
-#endif
-
         read_string(buf, buf_end, data_sections[i].name);
         read_uint32(buf, buf_end, data_sections[i].size);
-
+        CHECK_BUF(buf, buf_end, data_sections[i].size);
+        /* Temporary record data ptr for merge, will be replaced after the
+           merged_data_sections is mmapped */
+        if (data_sections[i].size > 0)
+            data_sections[i].data = (uint8 *)buf;
+        buf += data_sections[i].size;
+        total_size += align_uint64((uint64)data_sections[i].size, page_size);
+    }
+    if (total_size > UINT32_MAX) {
+        set_error_buf(error_buf, error_buf_size, "data sections too large");
+        return false;
+    }
+    if (total_size > 0) {
         /* Allocate memory for data */
-        if (data_sections[i].size > 0
-            && !(data_sections[i].data =
-                     os_mmap(NULL, data_sections[i].size, map_prot, map_flags,
-                             os_get_invalid_handle()))) {
-            set_error_buf(error_buf, error_buf_size, "allocate memory failed");
+        merged_sections = module->merged_data_sections =
+            loader_mmap((uint32)total_size, false, error_buf, error_buf_size);
+        if (!merged_sections) {
             return false;
         }
-#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
-#if !defined(BH_PLATFORM_LINUX_SGX) && !defined(BH_PLATFORM_WINDOWS) \
-    && !defined(BH_PLATFORM_DARWIN)
-        /* address must be in the first 2 Gigabytes of
-           the process address space */
-        bh_assert((uintptr_t)data_sections[i].data < INT32_MAX);
-#endif
-#endif
+        module->merged_data_sections_size = (uint32)total_size;
+    }
 
-        read_byte_array(buf, buf_end, data_sections[i].data,
-                        data_sections[i].size);
+    /* Second iteration: Create each data section */
+    for (i = 0; i < module->data_section_count; i++) {
+        if (data_sections[i].size > 0) {
+            bh_memcpy_s(merged_sections, data_sections[i].size,
+                        data_sections[i].data, data_sections[i].size);
+            data_sections[i].data = merged_sections;
+            merged_sections += align_uint(data_sections[i].size, page_size);
+        }
     }
 
     *p_buf = buf;
@@ -2532,6 +2566,82 @@ fail:
     return false;
 }
 
+#if !defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF)
+static bool
+try_merge_data_and_text(const uint8 **buf, const uint8 **buf_end,
+                        AOTModule *module, char *error_buf,
+                        uint32 error_buf_size)
+{
+    uint8 *old_buf = (uint8 *)*buf;
+    uint8 *old_end = (uint8 *)*buf_end;
+    size_t code_size = (size_t)(old_end - old_buf);
+    uint32 page_size = os_getpagesize();
+    uint64 total_size = 0;
+    uint32 i;
+    uint8 *sections;
+
+    if (code_size == 0) {
+        return true;
+    }
+
+    /* calculate the total memory needed */
+    total_size += align_uint64((uint64)code_size, page_size);
+    for (i = 0; i < module->data_section_count; ++i) {
+        total_size +=
+            align_uint64((uint64)module->data_sections[i].size, page_size);
+    }
+    /* distance between .data and .text should not be greater than 4GB
+       for some targets (e.g. arm64 reloc need < 4G distance) */
+    if (total_size > UINT32_MAX) {
+        return false;
+    }
+    /* code_size was checked and must be larger than 0 here */
+    bh_assert(total_size > 0);
+
+    sections = loader_mmap((uint32)total_size, false, NULL, 0);
+    if (!sections) {
+        /* merge failed but may be not critical for some targets */
+        return false;
+    }
+    /* change the code part to be executable */
+    if (os_mprotect(sections, code_size,
+                    MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC)
+        != 0) {
+        os_munmap(sections, (uint32)total_size);
+        return false;
+    }
+
+    module->merged_data_text_sections = sections;
+    module->merged_data_text_sections_size = (uint32)total_size;
+
+    /* order not essential just as compiler does: .text section first */
+    *buf = sections;
+    *buf_end = sections + code_size;
+    bh_memcpy_s(sections, code_size, old_buf, code_size);
+    os_munmap(old_buf, code_size);
+    sections += align_uint((uint32)code_size, page_size);
+
+    /* then migrate .data sections */
+    for (i = 0; i < module->data_section_count; ++i) {
+        AOTObjectDataSection *data_section = module->data_sections + i;
+        uint8 *old_data = data_section->data;
+        data_section->data = sections;
+        bh_memcpy_s(data_section->data, data_section->size, old_data,
+                    data_section->size);
+        sections += align_uint(data_section->size, page_size);
+    }
+    /* free the original data sections */
+    if (module->merged_data_sections) {
+        os_munmap(module->merged_data_sections,
+                  module->merged_data_sections_size);
+        module->merged_data_sections = NULL;
+        module->merged_data_sections_size = 0;
+    }
+
+    return true;
+}
+#endif /* ! defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF) */
+
 static bool
 load_text_section(const uint8 *buf, const uint8 *buf_end, AOTModule *module,
                   char *error_buf, uint32 error_buf_size)
@@ -3391,16 +3501,9 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
            + sizeof(uint64) * module->real_plt_count
            + sizeof(uint32) * module->float_plt_count;
     if (size > 0) {
-        map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
-        /* aot code and data in x86_64 must be in range 0 to 2G due to
-           relocation for R_X86_64_32/32S/PC32 */
-        map_flags = MMAP_MAP_32BIT;
-
         if (size > UINT32_MAX
-            || !(module->extra_plt_data =
-                     os_mmap(NULL, (uint32)size, map_prot, map_flags,
-                             os_get_invalid_handle()))) {
-            set_error_buf(error_buf, error_buf_size, "mmap memory failed");
+            || !(module->extra_plt_data = loader_mmap(
+                     (uint32)size, true, error_buf, error_buf_size))) {
             goto fail;
         }
         module->extra_plt_data_size = (uint32)size;
@@ -3512,19 +3615,12 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
         GOTItem *got_item = module->got_item_list;
         uint32 got_item_idx = 0;
 
-        map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE;
-        /* aot code and data in x86_64 must be in range 0 to 2G due to
-           relocation for R_X86_64_32/32S/PC32 */
-        map_flags = MMAP_MAP_32BIT;
-
         /* Create the GOT for func_ptrs, note that it is different from
            the .got section of a dynamic object file */
         size = (uint64)sizeof(void *) * got_item_count;
         if (size > UINT32_MAX
-            || !(module->got_func_ptrs =
-                     os_mmap(NULL, (uint32)size, map_prot, map_flags,
-                             os_get_invalid_handle()))) {
-            set_error_buf(error_buf, error_buf_size, "mmap memory failed");
+            || !(module->got_func_ptrs = loader_mmap(
+                     (uint32)size, false, error_buf, error_buf_size))) {
             goto fail;
         }
 
@@ -3749,6 +3845,17 @@ load_from_sections(AOTModule *module, AOTSection *sections,
                     return false;
                 break;
             case AOT_SECTION_TYPE_TEXT:
+#if !defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF)
+                /* try to merge .data and .text, with exceptions:
+                 * 1. XIP mode
+                 * 2. pre-mmapped module load from aot_load_from_sections()
+                 * 3. nuttx & esp-idf: have separate region for MMAP_PROT_EXEC
+                 */
+                if (!module->is_indirect_mode && is_load_from_file_buf)
+                    if (!try_merge_data_and_text(&buf, &buf_end, module,
+                                                 error_buf, error_buf_size))
+                        LOG_WARNING("merge .data and .text sections failed");
+#endif /* ! defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF) */
                 if (!load_text_section(buf, buf_end, module, error_buf,
                                        error_buf_size))
                     return false;
@@ -4065,37 +4172,16 @@ create_sections(AOTModule *module, const uint8 *buf, uint32 size,
 
             if (section_type == AOT_SECTION_TYPE_TEXT) {
                 if ((section_size > 0) && !module->is_indirect_mode) {
-                    int map_prot =
-                        MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
-#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \
-    || defined(BUILD_TARGET_RISCV64_LP64D)                       \
-    || defined(BUILD_TARGET_RISCV64_LP64)
-                    /* aot code and data in x86_64 must be in range 0 to 2G due
-                       to relocation for R_X86_64_32/32S/PC32 */
-                    int map_flags = MMAP_MAP_32BIT;
-#else
-                    int map_flags = MMAP_MAP_NONE;
-#endif
                     total_size =
                         (uint64)section_size + aot_get_plt_table_size();
                     total_size = (total_size + 3) & ~((uint64)3);
                     if (total_size >= UINT32_MAX
                         || !(aot_text =
-                                 os_mmap(NULL, (uint32)total_size, map_prot,
-                                         map_flags, os_get_invalid_handle()))) {
+                                 loader_mmap((uint32)total_size, true,
+                                             error_buf, error_buf_size))) {
                         wasm_runtime_free(section);
-                        set_error_buf(error_buf, error_buf_size,
-                                      "mmap memory failed");
                         goto fail;
                     }
-#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)
-#if !defined(BH_PLATFORM_LINUX_SGX) && !defined(BH_PLATFORM_WINDOWS) \
-    && !defined(BH_PLATFORM_DARWIN)
-                    /* address must be in the first 2 Gigabytes of
-                       the process address space */
-                    bh_assert((uintptr_t)aot_text < INT32_MAX);
-#endif
-#endif
 
 #if (WASM_MEM_DUAL_BUS_MIRROR != 0)
                     mirrored_text = os_get_dbus_mirror(aot_text);
@@ -4179,7 +4265,11 @@ load(const uint8 *buf, uint32 size, AOTModule *module,
     if (!ret) {
         /* If load_from_sections() fails, then aot text is destroyed
            in destroy_sections() */
-        destroy_sections(section_list, module->is_indirect_mode ? false : true);
+        destroy_sections(section_list,
+                         module->is_indirect_mode
+                                 || module->merged_data_text_sections
+                             ? false
+                             : true);
         /* aot_unload() won't destroy aot text again */
         module->code = NULL;
     }
@@ -4329,7 +4419,8 @@ aot_unload(AOTModule *module)
     }
 #endif
 
-    if (module->code && !module->is_indirect_mode) {
+    if (module->code && !module->is_indirect_mode
+        && !module->merged_data_text_sections) {
         /* The layout is: literal size + literal + code (with plt table) */
         uint8 *mmap_addr = module->literal - sizeof(uint32);
         uint32 total_size =
@@ -4364,6 +4455,14 @@ aot_unload(AOTModule *module)
         destroy_object_data_sections(module->data_sections,
                                      module->data_section_count);
 
+    if (module->merged_data_sections)
+        os_munmap(module->merged_data_sections,
+                  module->merged_data_sections_size);
+
+    if (module->merged_data_text_sections)
+        os_munmap(module->merged_data_text_sections,
+                  module->merged_data_text_sections_size);
+
 #if WASM_ENABLE_DEBUG_AOT != 0
     jit_code_entry_destroy(module->elf_hdr);
 #endif

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

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

@@ -315,6 +315,13 @@ typedef struct AOTModule {
 
     /* Whether the underlying wasm binary buffer can be freed */
     bool is_binary_freeable;
+
+    /* `.data` sections merged into one mmaped to reduce the tlb cache miss */
+    uint8 *merged_data_sections;
+    uint32 merged_data_sections_size;
+    /* `.data` and `.text` sections merged into one large mmaped section */
+    uint8 *merged_data_text_sections;
+    uint32 merged_data_text_sections_size;
 } AOTModule;
 
 #define AOTMemoryInstance WASMMemoryInstance
@@ -605,6 +612,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 - 0
core/iwasm/aot/arch/aot_reloc_riscv.c

@@ -134,6 +134,7 @@ static SymbolMap target_sym_map[] = {
     REG_SYM(__eqdf2),
     REG_SYM(__extendsfdf2),
     REG_SYM(__fixdfdi),
+    REG_SYM(__fixdfsi),
     REG_SYM(__fixunsdfdi),
     REG_SYM(__fixunsdfsi),
     REG_SYM(__floatdidf),
@@ -143,6 +144,7 @@ static SymbolMap target_sym_map[] = {
     REG_SYM(__gedf2),
     REG_SYM(__gtdf2),
     REG_SYM(__ledf2),
+    REG_SYM(__ltdf2),
     REG_SYM(__muldf3),
     REG_SYM(__nedf2),
     REG_SYM(__negdf2),

+ 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)
 {

+ 56 - 35
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
@@ -1417,12 +1419,39 @@ wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args,
                      char *error_buf, uint32 error_buf_size)
 {
     WASMModuleCommon *module_common = NULL;
+    uint32 package_type;
+    bool magic_header_detected = false;
 
     if (!args) {
+        set_error_buf(error_buf, error_buf_size,
+                      "WASM module load failed: null load arguments");
+        return NULL;
+    }
+
+    if (size < 4) {
+        set_error_buf(error_buf, error_buf_size,
+                      "WASM module load failed: unexpected end");
         return NULL;
     }
 
-    if (get_package_type(buf, size) == Wasm_Module_Bytecode) {
+    package_type = get_package_type(buf, size);
+    if (package_type == Wasm_Module_Bytecode) {
+#if WASM_ENABLE_INTERP != 0
+        magic_header_detected = true;
+#endif
+    }
+    else if (package_type == Wasm_Module_AoT) {
+#if WASM_ENABLE_AOT != 0
+        magic_header_detected = true;
+#endif
+    }
+    if (!magic_header_detected) {
+        set_error_buf(error_buf, error_buf_size,
+                      "WASM module load failed: magic header not detected");
+        return NULL;
+    }
+
+    if (package_type == Wasm_Module_Bytecode) {
 #if WASM_ENABLE_INTERP != 0
         module_common =
             (WASMModuleCommon *)wasm_load(buf, size,
@@ -1435,7 +1464,7 @@ wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args,
                 args->wasm_binary_freeable;
 #endif
     }
-    else if (get_package_type(buf, size) == Wasm_Module_AoT) {
+    else if (package_type == Wasm_Module_AoT) {
 #if WASM_ENABLE_AOT != 0
         module_common = (WASMModuleCommon *)aot_load_from_aot_file(
             buf, size, args, error_buf, error_buf_size);
@@ -1444,15 +1473,7 @@ wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args,
                 args->wasm_binary_freeable;
 #endif
     }
-    else {
-        if (size < 4)
-            set_error_buf(error_buf, error_buf_size,
-                          "WASM module load failed: unexpected end");
-        else
-            set_error_buf(error_buf, error_buf_size,
-                          "WASM module load failed: magic header not detected");
-        return NULL;
-    }
+
     if (!module_common) {
         LOG_DEBUG("WASM module load failed");
         return NULL;

+ 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;
 

+ 28 - 32
core/iwasm/compilation/aot_emit_const.c

@@ -68,23 +68,21 @@ aot_compile_op_f32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 {
     LLVMValueRef alloca, value;
 
-    if (!isnan(f32_const)) {
-        if (comp_ctx->is_indirect_mode
-            && aot_intrinsic_check_capability(comp_ctx, "f32.const")) {
-            WASMValue wasm_value;
-            memcpy(&wasm_value.f32, &f32_const, sizeof(float32));
-            value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol,
-                                              &wasm_value, VALUE_TYPE_F32);
-            if (!value) {
-                return false;
-            }
-            PUSH_F32(value);
-        }
-        else {
-            value = F32_CONST(f32_const);
-            CHECK_LLVM_CONST(value);
-            PUSH_F32(value);
+    if (comp_ctx->is_indirect_mode
+        && aot_intrinsic_check_capability(comp_ctx, "f32.const")) {
+        WASMValue wasm_value;
+        memcpy(&wasm_value.f32, &f32_const, sizeof(float32));
+        value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol,
+                                          &wasm_value, VALUE_TYPE_F32);
+        if (!value) {
+            return false;
         }
+        PUSH_F32(value);
+    }
+    else if (!isnan(f32_const)) {
+        value = F32_CONST(f32_const);
+        CHECK_LLVM_CONST(value);
+        PUSH_F32(value);
     }
     else {
         int32 i32_const;
@@ -123,23 +121,21 @@ aot_compile_op_f64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 {
     LLVMValueRef alloca, value;
 
-    if (!isnan(f64_const)) {
-        if (comp_ctx->is_indirect_mode
-            && aot_intrinsic_check_capability(comp_ctx, "f64.const")) {
-            WASMValue wasm_value;
-            memcpy(&wasm_value.f64, &f64_const, sizeof(float64));
-            value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol,
-                                              &wasm_value, VALUE_TYPE_F64);
-            if (!value) {
-                return false;
-            }
-            PUSH_F64(value);
-        }
-        else {
-            value = F64_CONST(f64_const);
-            CHECK_LLVM_CONST(value);
-            PUSH_F64(value);
+    if (comp_ctx->is_indirect_mode
+        && aot_intrinsic_check_capability(comp_ctx, "f64.const")) {
+        WASMValue wasm_value;
+        memcpy(&wasm_value.f64, &f64_const, sizeof(float64));
+        value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol,
+                                          &wasm_value, VALUE_TYPE_F64);
+        if (!value) {
+            return false;
         }
+        PUSH_F64(value);
+    }
+    else if (!isnan(f64_const)) {
+        value = F64_CONST(f64_const);
+        CHECK_LLVM_CONST(value);
+        PUSH_F64(value);
     }
     else {
         int64 i64_const;

+ 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;

+ 21 - 2
core/iwasm/compilation/aot_llvm.c

@@ -1690,7 +1690,15 @@ aot_create_stack_sizes(const AOTCompData *comp_data, AOTCompContext *comp_ctx)
      * avoid creating extra relocations in the precheck functions.
      */
     LLVMSetLinkage(stack_sizes, LLVMInternalLinkage);
-    LLVMSetSection(stack_sizes, aot_stack_sizes_section_name);
+    /*
+     * for AOT, place it into a dedicated section for the convenience
+     * of the AOT file generation and symbol resolutions.
+     *
+     * for JIT, it doesn't matter.
+     */
+    if (!comp_ctx->is_jit_mode) {
+        LLVMSetSection(stack_sizes, aot_stack_sizes_section_name);
+    }
     comp_ctx->stack_sizes_type = stack_sizes_type;
     comp_ctx->stack_sizes = stack_sizes;
     return true;
@@ -3108,6 +3116,16 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
         goto fail;
     }
 
+    /* Return error if ref-types and GC are disabled by command line but
+       ref-types instructions are used */
+    if (!option->enable_ref_types && !option->enable_gc
+        && wasm_module->is_ref_types_used) {
+        aot_set_last_error("ref-types instruction was found, "
+                           "try removing --disable-ref-types option "
+                           "or adding --enable-gc option.");
+        goto fail;
+    }
+
     /* Disable features when they are not actually used */
     if (!wasm_module->is_simd_used) {
         option->enable_simd = comp_ctx->enable_simd = false;
@@ -3121,7 +3139,8 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
 #endif
 
     if (option->enable_simd && strcmp(comp_ctx->target_arch, "x86_64") != 0
-        && strncmp(comp_ctx->target_arch, "aarch64", 7) != 0) {
+        && strncmp(comp_ctx->target_arch, "aarch64", 7) != 0
+        && strcmp(comp_ctx->target_arch, "arc") != 0) {
         /* Disable simd if it isn't supported by target arch */
         option->enable_simd = false;
     }

+ 6 - 0
core/iwasm/compilation/aot_llvm_extra.cpp

@@ -41,6 +41,9 @@
 #include <llvm/Target/CodeGenCWrappers.h>
 #include <llvm/Target/TargetMachine.h>
 #include <llvm/Target/TargetOptions.h>
+#if LLVM_VERSION_MAJOR >= 17
+#include <llvm/TargetParser/Triple.h>
+#endif
 #include <llvm/Transforms/Utils/LowerMemIntrinsics.h>
 #include <llvm/Transforms/Vectorize/LoopVectorize.h>
 #include <llvm/Transforms/Vectorize/LoadStoreVectorizer.h>
@@ -173,6 +176,9 @@ aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str)
     else if (targetArch == llvm::Triple::aarch64) {
         return subTargetInfo->checkFeatures("+neon");
     }
+    else if (targetArch == llvm::Triple::arc) {
+        return true;
+    }
     else {
         return false;
     }

+ 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

+ 112 - 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;
@@ -2281,8 +2323,15 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 bh_assert(tidx < module->module->type_count);
                 cur_type = wasm_types[tidx];
 
+                /* clang-format off */
+#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
                 read_leb_uint32(frame_ip, frame_ip_end, tbl_idx);
+#else
+                frame_ip++;
+                tbl_idx = 0;
+#endif
                 bh_assert(tbl_idx < module->table_count);
+                /* clang-format on */
 
                 tbl_inst = wasm_get_table_inst(module, tbl_idx);
 
@@ -4285,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();
             }
 
@@ -4301,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();
             }
 
@@ -4316,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();
             }
 
@@ -4331,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();
             }
 
@@ -4346,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();
             }
 
@@ -4361,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();
             }
 
@@ -4376,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();
             }
 
@@ -4391,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();
             }
 
@@ -4406,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();
             }
 
@@ -4421,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();
             }
 
@@ -4436,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();
             }
 
@@ -4452,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();
             }
 
@@ -4469,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();
@@ -4484,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();
             }
 
@@ -4494,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();
@@ -4512,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();
             }
 
@@ -4524,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();
@@ -4538,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();
             }
 
@@ -4551,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();
@@ -4569,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);
                 }
@@ -4607,7 +4638,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 #endif
                 }
 
-                (void)reserved;
                 HANDLE_OP_END();
             }
 
@@ -5603,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();
@@ -5653,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 */
@@ -5691,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;

+ 84 - 39
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;
@@ -7149,10 +7199,10 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
             case WASM_OP_RETURN_CALL_INDIRECT:
 #endif
                 skip_leb_uint32(p, p_end); /* typeidx */
-#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0
-                u8 = read_uint8(p); /* 0x00 */
-#else
+#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
                 skip_leb_uint32(p, p_end); /* tableidx */
+#else
+                u8 = read_uint8(p); /* 0x00 */
 #endif
                 break;
 
@@ -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;
 
@@ -12004,6 +12052,16 @@ re_scan:
 
                 read_leb_uint32(p, p_end, type_idx);
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
+#if WASM_ENABLE_WAMR_COMPILER != 0
+                if (p + 1 < p_end && *p != 0x00) {
+                    /*
+                     * Any non-0x00 byte requires the ref types proposal.
+                     * This is different from checking the table_idx value
+                     * since `0x80 0x00` etc. are all valid encodings of zero.
+                     */
+                    module->is_ref_types_used = true;
+                }
+#endif
                 read_leb_uint32(p, p_end, table_idx);
 #else
                 CHECK_BUF(p, p_end, 1);
@@ -13145,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)) {
@@ -13211,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;
@@ -13227,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;
@@ -14584,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,
@@ -14634,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)
@@ -14656,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;
@@ -14674,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,
@@ -14911,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

+ 72 - 36
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;
@@ -3501,8 +3545,11 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
             case WASM_OP_RETURN_CALL_INDIRECT:
 #endif
                 skip_leb_uint32(p, p_end); /* typeidx */
-                CHECK_BUF(p, p_end, 1);
+#if WASM_ENABLE_REF_TYPES != 0
+                skip_leb_uint32(p, p_end); /* tableidx */
+#else
                 u8 = read_uint8(p); /* 0x00 */
+#endif
                 break;
 
 #if WASM_ENABLE_EXCE_HANDLING != 0
@@ -3582,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:
@@ -3755,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
@@ -5902,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;
@@ -7264,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);
@@ -7326,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;
@@ -7339,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;
@@ -7679,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);
@@ -7716,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();
@@ -7735,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[]);

+ 3 - 2
core/shared/platform/include/platform_api_extension.h

@@ -104,8 +104,9 @@ os_thread_exit(void *retval);
 #endif
 
 /* Clang's __GNUC_PREREQ macro has a different meaning than GCC one,
-   so we have to handle this case specially */
-#if defined(__clang__)
+   so we have to handle this case specially(except the CCAC compiler
+   provided by MetaWare, which doesn't support atomic operations) */
+#if defined(__clang__) && !defined(__CCAC__)
 /* Clang provides stdatomic.h since 3.6.0
    See https://releases.llvm.org/3.6.0/tools/clang/docs/ReleaseNotes.html */
 #if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 6)

+ 1 - 0
core/shared/utils/bh_atomic.h

@@ -6,6 +6,7 @@
 #ifndef _BH_ATOMIC_H
 #define _BH_ATOMIC_H
 
+#include "bh_platform.h"
 #include "gnuc.h"
 
 #ifdef __cplusplus

+ 1 - 1
core/version.h

@@ -7,5 +7,5 @@
 #define _WAMR_VERSION_H_
 #define WAMR_VERSION_MAJOR 2
 #define WAMR_VERSION_MINOR 1
-#define WAMR_VERSION_PATCH 1
+#define WAMR_VERSION_PATCH 2
 #endif

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

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

@@ -62,6 +62,13 @@ aot_target_options_map = {
     "xtensa": ["--target=xtensa"],
 }
 
+# AOT compilation options mapping for XIP mode
+aot_target_options_map_xip = {
+    # avoid l32r relocations for xtensa
+    "xtensa": ["--mllvm=-mtext-section-literals"],
+    "riscv32_ilp32f": ["--enable-builtin-intrinsics=i64.common,f64.common,f32.const,f64.const,f64xi32,f64xi64,f64_promote_f32,f32_demote_f64"],
+}
+
 def debug(data):
     if debug_file:
         debug_file.write(data)
@@ -320,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")
 
@@ -1090,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 ]
@@ -1122,10 +1134,8 @@ def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r, output = '
 
     if opts.xip:
         cmd.append("--xip")
-
-        # avoid l32r relocations for xtensa
-        if opts.target == "xtensa":
-            cmd.append("--mllvm=-mtext-section-literals")
+        if test_target in aot_target_options_map_xip:
+            cmd += aot_target_options_map_xip[test_target]
 
     if opts.multi_thread:
         cmd.append("--enable-multi-thread")
@@ -1308,8 +1318,8 @@ if __name__ == "__main__":
     if test_aot:
         aot_tempfile = create_tmp_file(".aot")
         # could be potientially compiled to aot
-        # with the future following call test_assert_xxx, 
-        # add them to temp_file_repo now even if no actual following file, 
+        # with the future following call test_assert_xxx,
+        # add them to temp_file_repo now even if no actual following file,
         # it will be simple ignore during final deletion if not exist
         prefix = wasm_tempfile.split(".wasm")[0]
         temp_file_repo.append(prefix + ".aot")
@@ -1436,8 +1446,8 @@ if __name__ == "__main__":
                         if test_aot:
                             r = compile_wasm_to_aot(temp_files[1], temp_files[2], True, opts, r)
                             # could be potientially compiled to aot
-                            # with the future following call test_assert_xxx, 
-                            # add them to temp_file_repo now even if no actual following file, 
+                            # with the future following call test_assert_xxx,
+                            # add them to temp_file_repo now even if no actual following file,
                             # it will be simple ignore during final deletion if not exist
                             prefix = temp_files[1].split(".wasm")[0]
                             temp_file_repo.append(prefix + ".aot")

+ 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