From 1329e1d3e1c9d693bdbb72c229ebad469e2d1381 Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Wed, 21 Aug 2024 12:22:23 +0800 Subject: [PATCH 1/5] Add support for multi-memory proposal in classic interpreter (#3742) Implement multi-memory for classic-interpreter. Support core spec (and bulk memory) opcodes now, and will support atomic opcodes, and add multi-memory export APIs in the future. PS: Multi-memory spec test patched a lot for linking test to adapt for multi-module implementation. --- .../compilation_on_android_ubuntu.yml | 44 +- .github/workflows/nightly_run.yml | 26 +- build-scripts/config_common.cmake | 5 + core/config.h | 5 + core/iwasm/aot/aot_runtime.c | 7 + core/iwasm/aot/aot_runtime.h | 4 + core/iwasm/common/wasm_memory.c | 38 +- core/iwasm/common/wasm_runtime_common.c | 50 +- core/iwasm/compilation/aot.c | 1 + core/iwasm/compilation/aot_emit_memory.c | 1 + core/iwasm/fast-jit/fe/jit_emit_memory.c | 1 + core/iwasm/interpreter/wasm.h | 8 + core/iwasm/interpreter/wasm_interp_classic.c | 156 ++- core/iwasm/interpreter/wasm_interp_fast.c | 1 + core/iwasm/interpreter/wasm_loader.c | 107 +- core/iwasm/interpreter/wasm_mini_loader.c | 103 +- core/iwasm/interpreter/wasm_runtime.c | 259 +++-- core/iwasm/interpreter/wasm_runtime.h | 7 + .../wamr-test-suites/spec-test-script/all.py | 26 +- .../multi_memory_ignore_cases.patch | 1022 +++++++++++++++++ .../spec-test-script/runtest.py | 5 + tests/wamr-test-suites/test_wamr.sh | 44 +- 22 files changed, 1658 insertions(+), 262 deletions(-) create mode 100644 tests/wamr-test-suites/spec-test-script/multi_memory_ignore_cases.patch diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index d4d32908..249a6f27 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.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 }} diff --git a/.github/workflows/nightly_run.yml b/.github/workflows/nightly_run.yml index 204cadc9..f39085d0 100644 --- a/.github/workflows/nightly_run.yml +++ b/.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: | diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index d4dd2c0d..252ba3a8 100644 --- a/build-scripts/config_common.cmake +++ b/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 () diff --git a/core/config.h b/core/config.h index 7c783dd3..a25eb543 100644 --- a/core/config.h +++ b/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 diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 7e8799e0..3ca26114 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/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) diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index e3704f82..d109ceba 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -605,6 +605,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 */ diff --git a/core/iwasm/common/wasm_memory.c b/core/iwasm/common/wasm_memory.c index 187b4de0..71d33754 100644 --- a/core/iwasm/common/wasm_memory.c +++ b/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) { diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index f54ac4ab..5dd2957d 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/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 diff --git a/core/iwasm/compilation/aot.c b/core/iwasm/compilation/aot.c index 2bacae87..0a2cae1f 100644 --- a/core/iwasm/compilation/aot.c +++ b/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; diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c index e7c9b679..806150ff 100644 --- a/core/iwasm/compilation/aot_emit_memory.c +++ b/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; diff --git a/core/iwasm/fast-jit/fe/jit_emit_memory.c b/core/iwasm/fast-jit/fe/jit_emit_memory.c index ea245ba3..bbe82cf6 100644 --- a/core/iwasm/fast-jit/fe/jit_emit_memory.c +++ b/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; } diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index ea93adb0..e043465d 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/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 diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 766d2f52..4a8ba4e2 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -697,6 +697,44 @@ wasm_interp_get_frame_ref(WASMInterpFrame *frame) #define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res) #endif +#if WASM_ENABLE_MULTI_MEMORY != 0 +/* If the current memidx differs than the last cached one, + * update memory related information */ +#define read_leb_memidx(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (res != memidx_cached) { \ + memory = wasm_get_memory_with_idx(module, res); \ + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); \ + memidx_cached = res; \ + } \ + } while (0) +/* First read the alignment, then if it has flag indicating following memidx, + * read and update memory related information, if it differs than the + * last(cached) one. If it doesn't have flag reset the + * memory instance to the default memories[0] */ +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (!(res & OPT_MEMIDX_FLAG)) \ + memidx = 0; \ + else \ + read_leb_uint32(p, p_end, memidx); \ + if (memidx != memidx_cached) { \ + memory = wasm_get_memory_with_idx(module, memidx); \ + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); \ + memidx_cached = memidx; \ + } \ + } while (0) +#else +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + (void)res; \ + } while (0) +#define read_leb_memidx(p, p_end, res) read_leb_memarg(p, p_end, res) +#endif + #if WASM_ENABLE_LABELS_AS_VALUES == 0 #define RECOVER_FRAME_IP_END() frame_ip_end = wasm_get_func_code_end(cur_func) #else @@ -1567,6 +1605,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, if (memory) is_memory64 = memory->is_memory64; #endif +#if WASM_ENABLE_MULTI_MEMORY != 0 + uint32 memidx = 0; + uint32 memidx_cached = (uint32)-1; +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 uint8 *frame_ip_orig = NULL; @@ -4292,13 +4334,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(4); PUSH_I32(LOAD_I32(maddr)); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4308,13 +4349,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(8); PUSH_I64(LOAD_I64(maddr)); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4323,13 +4363,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(1); PUSH_I32(sign_ext_8_32(*(int8 *)maddr)); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4338,13 +4377,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(1); PUSH_I32((uint32)(*(uint8 *)maddr)); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4353,13 +4391,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(2); PUSH_I32(sign_ext_16_32(LOAD_I16(maddr))); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4368,13 +4405,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(2); PUSH_I32((uint32)(LOAD_U16(maddr))); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4383,13 +4419,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(1); PUSH_I64(sign_ext_8_64(*(int8 *)maddr)); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4398,13 +4433,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(1); PUSH_I64((uint64)(*(uint8 *)maddr)); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4413,13 +4447,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(2); PUSH_I64(sign_ext_16_64(LOAD_I16(maddr))); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4428,13 +4461,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(2); PUSH_I64((uint64)(LOAD_U16(maddr))); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4443,14 +4475,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - opcode = *(frame_ip - 1); - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(4); PUSH_I64(sign_ext_32_64(LOAD_I32(maddr))); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4459,13 +4489,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(4); PUSH_I64((uint64)(LOAD_U32(maddr))); CHECK_READ_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4476,7 +4505,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); frame_sp--; addr = POP_MEM_OFFSET(); @@ -4491,7 +4520,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, STORE_U32(maddr, frame_sp[1]); } CHECK_WRITE_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4501,7 +4529,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 flags; mem_offset_t offset, addr; - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); frame_sp -= 2; addr = POP_MEM_OFFSET(); @@ -4519,7 +4547,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, GET_I64_FROM_ADDR(frame_sp + 1)); } CHECK_WRITE_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4531,7 +4558,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 sval; opcode = *(frame_ip - 1); - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); sval = (uint32)POP_I32(); addr = POP_MEM_OFFSET(); @@ -4545,7 +4572,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, STORE_U16(maddr, (uint16)sval); } CHECK_WRITE_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } @@ -4558,7 +4584,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint64 sval; opcode = *(frame_ip - 1); - read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_memarg(frame_ip, frame_ip_end, flags); read_leb_mem_offset(frame_ip, frame_ip_end, offset); sval = (uint64)POP_I64(); addr = POP_MEM_OFFSET(); @@ -4576,29 +4602,27 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, STORE_U32(maddr, (uint32)sval); } CHECK_WRITE_WATCHPOINT(addr, offset); - (void)flags; HANDLE_OP_END(); } /* memory size and memory grow instructions */ HANDLE_OP(WASM_OP_MEMORY_SIZE) { - uint32 reserved; - read_leb_uint32(frame_ip, frame_ip_end, reserved); + uint32 mem_idx; + read_leb_memidx(frame_ip, frame_ip_end, mem_idx); PUSH_PAGE_COUNT(memory->cur_page_count); - (void)reserved; HANDLE_OP_END(); } HANDLE_OP(WASM_OP_MEMORY_GROW) { - uint32 reserved, delta, - prev_page_count = memory->cur_page_count; + uint32 mem_idx, delta, prev_page_count; - read_leb_uint32(frame_ip, frame_ip_end, reserved); + read_leb_memidx(frame_ip, frame_ip_end, mem_idx); + prev_page_count = memory->cur_page_count; delta = (uint32)POP_PAGE_COUNT(); - if (!wasm_enlarge_memory(module, delta)) { + if (!wasm_enlarge_memory_with_idx(module, delta, mem_idx)) { /* failed to memory.grow, return -1 */ PUSH_PAGE_COUNT(-1); } @@ -4614,7 +4638,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, #endif } - (void)reserved; HANDLE_OP_END(); } @@ -5610,8 +5633,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint8 *data; read_leb_uint32(frame_ip, frame_ip_end, segment); +#if WASM_ENABLE_MULTI_MEMORY != 0 + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else /* skip memory index */ frame_ip++; +#endif bytes = (uint64)(uint32)POP_I32(); offset = (uint64)(uint32)POP_I32(); @@ -5660,33 +5687,54 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { mem_offset_t dst, src, len; uint8 *mdst, *msrc; + uint64 dlen; - frame_ip += 2; len = POP_MEM_OFFSET(); src = POP_MEM_OFFSET(); dst = POP_MEM_OFFSET(); +#if WASM_ENABLE_MULTI_MEMORY != 0 + /* dst memidx */ + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip dst memidx */ + frame_ip += 1; +#endif #if WASM_ENABLE_THREAD_MGR != 0 linear_mem_size = get_linear_mem_size(); #endif - + /* dst boundary check */ #ifndef OS_ENABLE_HW_BOUND_CHECK - CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); #else - if ((uint64)(uint32)src + len > linear_mem_size) + if ((uint64)dst + len > linear_mem_size) goto out_of_bounds; - msrc = memory->memory_data + (uint32)src; + mdst = memory->memory_data + dst; +#endif + dlen = linear_mem_size - dst; - if ((uint64)(uint32)dst + len > linear_mem_size) +#if WASM_ENABLE_MULTI_MEMORY != 0 + /* src memidx */ + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip src memidx */ + frame_ip += 1; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + /* src boundary check */ +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); +#else + if ((uint64)src + len > linear_mem_size) goto out_of_bounds; - mdst = memory->memory_data + (uint32)dst; + msrc = memory->memory_data + src; #endif - /* allowing the destination and source to overlap */ #if WASM_ENABLE_MEMORY64 == 0 - bh_memmove_s(mdst, (uint32)(linear_mem_size - dst), - msrc, (uint32)len); + /* allowing the destination and source to overlap */ + bh_memmove_s(mdst, (uint32)dlen, msrc, (uint32)len); #else /* use memmove when memory64 is enabled since len may be larger than UINT32_MAX */ @@ -5698,7 +5746,13 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, { mem_offset_t dst, len; uint8 fill_val, *mdst; + +#if WASM_ENABLE_MULTI_MEMORY != 0 + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip memory index */ frame_ip++; +#endif len = POP_MEM_OFFSET(); fill_val = POP_I32(); diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 82507924..51963759 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/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; diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 0891598d..13947ac8 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -127,6 +127,17 @@ check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length, #define skip_leb_uint32(p, p_end) skip_leb(p) #define skip_leb_int32(p, p_end) skip_leb(p) #define skip_leb_mem_offset(p, p_end) skip_leb(p) +#define skip_leb_memidx(p, p_end) skip_leb(p) +#if WASM_ENABLE_MULTI_MEMORY == 0 +#define skip_leb_align(p, p_end) skip_leb(p) +#else +/* Skip the following memidx if applicable */ +#define skip_leb_align(p, p_end) \ + do { \ + if (*p++ & OPT_MEMIDX_FLAG) \ + skip_leb_uint32(p, p_end); \ + } while (0) +#endif #define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p) #define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) @@ -173,6 +184,40 @@ check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length, res = (int32)res64; \ } while (0) +#define read_leb_memidx(p, p_end, res) read_leb_uint32(p, p_end, res) +#if WASM_ENABLE_MULTI_MEMORY != 0 +#define check_memidx(module, memidx) \ + do { \ + if (memidx >= module->import_memory_count + module->memory_count) { \ + set_error_buf_v(error_buf, error_buf_size, "unknown memory %d", \ + memidx); \ + goto fail; \ + } \ + } while (0) +/* Bit 6(0x40) indicating the optional memidx, and reset bit 6 for + * alignment check */ +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (res & OPT_MEMIDX_FLAG) { \ + res &= ~OPT_MEMIDX_FLAG; \ + read_leb_uint32(p, p_end, memidx); /* memidx */ \ + check_memidx(module, memidx); \ + } \ + } while (0) +#else +/* reserved byte 0x00 */ +#define check_memidx(module, memidx) \ + do { \ + (void)module; \ + if (memidx != 0) { \ + set_error_buf(error_buf, error_buf_size, "zero byte expected"); \ + goto fail; \ + } \ + } while (0) +#define read_leb_memarg(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif + static char * type2str(uint8 type) { @@ -3288,11 +3333,13 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (flags & 1) read_leb_uint32(p, p_end, u32); module->import_memory_count++; +#if WASM_ENABLE_MULTI_MEMORY == 0 if (module->import_memory_count > 1) { set_error_buf(error_buf, error_buf_size, "multiple memories"); return false; } +#endif break; #if WASM_ENABLE_TAGS != 0 @@ -3903,11 +3950,14 @@ load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMMemory *memory; read_leb_uint32(p, p_end, memory_count); + +#if WASM_ENABLE_MULTI_MEMORY == 0 /* a total of one memory is allowed */ if (module->import_memory_count + memory_count > 1) { set_error_buf(error_buf, error_buf_size, "multiple memories"); return false; } +#endif if (memory_count) { module->memory_count = memory_count; @@ -7258,13 +7308,13 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, case WASM_OP_I64_STORE8: case WASM_OP_I64_STORE16: case WASM_OP_I64_STORE32: - skip_leb_uint32(p, p_end); /* align */ + skip_leb_align(p, p_end); /* align */ skip_leb_mem_offset(p, p_end); /* offset */ break; case WASM_OP_MEMORY_SIZE: case WASM_OP_MEMORY_GROW: - skip_leb_uint32(p, p_end); /* 0x00 */ + skip_leb_memidx(p, p_end); /* memidx */ break; case WASM_OP_I32_CONST: @@ -7562,19 +7612,17 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, #if WASM_ENABLE_BULK_MEMORY != 0 case WASM_OP_MEMORY_INIT: skip_leb_uint32(p, p_end); - /* skip memory idx */ - p++; + skip_leb_memidx(p, p_end); break; case WASM_OP_DATA_DROP: skip_leb_uint32(p, p_end); break; case WASM_OP_MEMORY_COPY: - /* skip two memory idx */ - p += 2; + skip_leb_memidx(p, p_end); + skip_leb_memidx(p, p_end); break; case WASM_OP_MEMORY_FILL: - /* skip memory idx */ - p++; + skip_leb_memidx(p, p_end); break; #endif /* WASM_ENABLE_BULK_MEMORY */ #if WASM_ENABLE_REF_TYPES != 0 @@ -7701,7 +7749,6 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, #if WASM_ENABLE_SHARED_MEMORY != 0 case WASM_OP_ATOMIC_PREFIX: { - /* TODO: memory64 offset type changes */ uint32 opcode1; /* atomic_op (u32_leb) + memarg (2 u32_leb) */ @@ -10876,6 +10923,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, #else mem_offset_type = VALUE_TYPE_I32; #endif + uint32 memidx; global_count = module->import_global_count + module->global_count; @@ -13155,7 +13203,7 @@ re_scan: } #endif CHECK_MEMORY(); - read_leb_uint32(p, p_end, align); /* align */ + read_leb_memarg(p, p_end, align); /* align */ read_leb_mem_offset(p, p_end, mem_offset); /* offset */ if (!check_memory_access_align(opcode, align, error_buf, error_buf_size)) { @@ -13221,12 +13269,8 @@ re_scan: case WASM_OP_MEMORY_SIZE: CHECK_MEMORY(); - /* reserved byte 0x00 */ - if (*p++ != 0x00) { - set_error_buf(error_buf, error_buf_size, - "zero byte expected"); - goto fail; - } + read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); PUSH_PAGE_COUNT(); module->possible_memory_grow = true; @@ -13237,12 +13281,8 @@ re_scan: case WASM_OP_MEMORY_GROW: CHECK_MEMORY(); - /* reserved byte 0x00 */ - if (*p++ != 0x00) { - set_error_buf(error_buf, error_buf_size, - "zero byte expected"); - goto fail; - } + read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); POP_AND_PUSH(mem_offset_type, mem_offset_type); module->possible_memory_grow = true; @@ -14594,8 +14634,8 @@ re_scan: && module->memory_count == 0) goto fail_unknown_memory; - if (*p++ != 0x00) - goto fail_zero_byte_expected; + read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); if (data_seg_idx >= module->data_seg_count) { set_error_buf_v(error_buf, error_buf_size, @@ -14644,10 +14684,11 @@ re_scan: case WASM_OP_MEMORY_COPY: { CHECK_BUF(p, p_end, sizeof(int16)); - /* both src and dst memory index should be 0 */ - if (*(int16 *)p != 0x0000) - goto fail_zero_byte_expected; - p += 2; + /* check both src and dst memory index */ + read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); + read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); if (module->import_memory_count == 0 && module->memory_count == 0) @@ -14666,9 +14707,8 @@ re_scan: } case WASM_OP_MEMORY_FILL: { - if (*p++ != 0x00) { - goto fail_zero_byte_expected; - } + read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); if (module->import_memory_count == 0 && module->memory_count == 0) { goto fail_unknown_memory; @@ -14684,10 +14724,6 @@ re_scan: #endif break; } - fail_zero_byte_expected: - set_error_buf(error_buf, error_buf_size, - "zero byte expected"); - goto fail; fail_unknown_memory: set_error_buf(error_buf, error_buf_size, @@ -14921,7 +14957,6 @@ re_scan: #if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) case WASM_OP_SIMD_PREFIX: { - /* TODO: memory64 offset type changes */ uint32 opcode1; #if WASM_ENABLE_WAMR_COMPILER != 0 diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index 0bb2f34e..8826f98d 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -62,6 +62,17 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) #define skip_leb_uint32(p, p_end) skip_leb(p) #define skip_leb_int32(p, p_end) skip_leb(p) #define skip_leb_mem_offset(p, p_end) skip_leb(p) +#define skip_leb_memidx(p, p_end) skip_leb(p) +#if WASM_ENABLE_MULTI_MEMORY == 0 +#define skip_leb_align(p, p_end) skip_leb(p) +#else +/* Skip the following memidx if applicable */ +#define skip_leb_align(p, p_end) \ + do { \ + if (*p++ & OPT_MEMIDX_FLAG) \ + skip_leb_uint32(p, p_end); \ + } while (0) +#endif static bool is_32bit_type(uint8 type) @@ -132,6 +143,35 @@ is_byte_a_type(uint8 type) #else #define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res) #endif +#define read_leb_memidx(p, p_end, res) read_leb_uint32(p, p_end, res) +#if WASM_ENABLE_MULTI_MEMORY != 0 +#define check_memidx(module, memidx) \ + do { \ + bh_assert(memidx \ + < module->import_memory_count + module->memory_count); \ + (void)memidx; \ + } while (0) +/* Bit 6 indicating the optional memidx, and reset bit 6 for + * alignment check */ +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (res & OPT_MEMIDX_FLAG) { \ + res &= ~OPT_MEMIDX_FLAG; \ + read_leb_uint32(p, p_end, memidx); /* memidx */ \ + check_memidx(module, memidx); \ + } \ + } while (0) +#else +/* reserved byte 0x00 */ +#define check_memidx(module, memidx) \ + do { \ + (void)module; \ + bh_assert(memidx == 0); \ + (void)memidx; \ + } while (0) +#define read_leb_memarg(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif static void * loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) @@ -882,7 +922,9 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (flags & 1) read_leb_uint32(p, p_end, u32); module->import_memory_count++; +#if WASM_ENABLE_MULTI_MEMORY != 0 bh_assert(module->import_memory_count <= 1); +#endif break; case IMPORT_KIND_GLOBAL: /* import global */ @@ -1223,7 +1265,9 @@ load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMMemory *memory; read_leb_uint32(p, p_end, memory_count); +#if WASM_ENABLE_MULTI_MEMORY != 0 bh_assert(module->import_memory_count + memory_count <= 1); +#endif if (memory_count) { module->memory_count = memory_count; @@ -3585,13 +3629,13 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, case WASM_OP_I64_STORE8: case WASM_OP_I64_STORE16: case WASM_OP_I64_STORE32: - skip_leb_uint32(p, p_end); /* align */ + skip_leb_align(p, p_end); /* align */ skip_leb_mem_offset(p, p_end); /* offset */ break; case WASM_OP_MEMORY_SIZE: case WASM_OP_MEMORY_GROW: - skip_leb_uint32(p, p_end); /* 0x00 */ + skip_leb_memidx(p, p_end); /* memidx */ break; case WASM_OP_I32_CONST: @@ -3758,19 +3802,17 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, #if WASM_ENABLE_BULK_MEMORY != 0 case WASM_OP_MEMORY_INIT: skip_leb_uint32(p, p_end); - /* skip memory idx */ - p++; + skip_leb_memidx(p, p_end); break; case WASM_OP_DATA_DROP: skip_leb_uint32(p, p_end); break; case WASM_OP_MEMORY_COPY: - /* skip two memory idx */ - p += 2; + skip_leb_memidx(p, p_end); + skip_leb_memidx(p, p_end); break; case WASM_OP_MEMORY_FILL: - /* skip memory idx */ - p++; + skip_leb_memidx(p, p_end); break; #endif #if WASM_ENABLE_REF_TYPES != 0 @@ -5905,7 +5947,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, uint8 *param_types, *local_types, local_type, global_type, mem_offset_type; BlockType func_block_type; uint16 *local_offsets, local_offset; - uint32 count, local_idx, global_idx, u32, align, i; + uint32 count, local_idx, global_idx, u32, align, i, memidx; mem_offset_t mem_offset; int32 i32, i32_const = 0; int64 i64_const; @@ -7267,7 +7309,7 @@ re_scan: } #endif CHECK_MEMORY(); - read_leb_uint32(p, p_end, align); /* align */ + read_leb_memarg(p, p_end, align); /* align */ read_leb_mem_offset(p, p_end, mem_offset); /* offset */ #if WASM_ENABLE_FAST_INTERP != 0 emit_uint32(loader_ctx, mem_offset); @@ -7329,9 +7371,8 @@ re_scan: case WASM_OP_MEMORY_SIZE: CHECK_MEMORY(); - /* reserved byte 0x00 */ - bh_assert(*p == 0x00); - p++; + read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); PUSH_PAGE_COUNT(); module->possible_memory_grow = true; @@ -7342,9 +7383,8 @@ re_scan: case WASM_OP_MEMORY_GROW: CHECK_MEMORY(); - /* reserved byte 0x00 */ - bh_assert(*p == 0x00); - p++; + read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); POP_AND_PUSH(mem_offset_type, mem_offset_type); module->possible_memory_grow = true; @@ -7682,16 +7722,13 @@ re_scan: #if WASM_ENABLE_BULK_MEMORY != 0 case WASM_OP_MEMORY_INIT: { + CHECK_MEMORY(); read_leb_uint32(p, p_end, segment_index); #if WASM_ENABLE_FAST_INTERP != 0 emit_uint32(loader_ctx, segment_index); #endif - bh_assert(module->import_memory_count - + module->memory_count - > 0); - - bh_assert(*p == 0x00); - p++; + read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); bh_assert(segment_index < module->data_seg_count); bh_assert(module->data_seg_count1 > 0); @@ -7719,14 +7756,13 @@ re_scan: } case WASM_OP_MEMORY_COPY: { + CHECK_MEMORY(); CHECK_BUF(p, p_end, sizeof(int16)); - /* both src and dst memory index should be 0 */ - bh_assert(*(int16 *)p == 0x0000); - p += 2; - - bh_assert(module->import_memory_count - + module->memory_count - > 0); + /* check both src and dst memory index */ + read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); POP_MEM_OFFSET(); POP_MEM_OFFSET(); @@ -7738,12 +7774,9 @@ re_scan: } case WASM_OP_MEMORY_FILL: { - bh_assert(*p == 0); - p++; - - bh_assert(module->import_memory_count - + module->memory_count - > 0); + CHECK_MEMORY(); + read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); POP_MEM_OFFSET(); POP_I32(); diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index f3b9b2e8..f70f9cb7 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/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; - } + /* 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 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; - } - - 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; - } - 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++; + 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 (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; - /* 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; + 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; #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 * diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 80aee874..8666541f 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/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[]); diff --git a/tests/wamr-test-suites/spec-test-script/all.py b/tests/wamr-test-suites/spec-test-script/all.py index fcc5ff15..005874ee 100644 --- a/tests/wamr-test-suites/spec-test-script/all.py +++ b/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, diff --git a/tests/wamr-test-suites/spec-test-script/multi_memory_ignore_cases.patch b/tests/wamr-test-suites/spec-test-script/multi_memory_ignore_cases.patch new file mode 100644 index 00000000..38b33175 --- /dev/null +++ b/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) diff --git a/tests/wamr-test-suites/spec-test-script/runtest.py b/tests/wamr-test-suites/spec-test-script/runtest.py index 291718b6..97820eaa 100755 --- a/tests/wamr-test-suites/spec-test-script/runtest.py +++ b/tests/wamr-test-suites/spec-test-script/runtest.py @@ -327,6 +327,9 @@ parser.add_argument('--gc', default=False, action='store_true', parser.add_argument('--memory64', default=False, action='store_true', help='Test with Memory64') +parser.add_argument('--multi-memory', default=False, action='store_true', + help='Test with multi-memory(with multi-module auto enabled)') + parser.add_argument('--qemu', default=False, action='store_true', help="Enable QEMU") @@ -1097,6 +1100,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts): cmd = [opts.wast2wasm, "--enable-threads", "--no-check", "--enable-exceptions", "--enable-tail-call", wast_tempfile, "-o", wasm_tempfile ] elif opts.memory64: cmd = [opts.wast2wasm, "--enable-memory64", "--no-check", wast_tempfile, "-o", wasm_tempfile ] + elif opts.multi_memory: + cmd = [opts.wast2wasm, "--enable-multi-memory", "--no-check", wast_tempfile, "-o", wasm_tempfile ] else: cmd = [opts.wast2wasm, "--enable-threads", "--no-check", wast_tempfile, "-o", wasm_tempfile ] diff --git a/tests/wamr-test-suites/test_wamr.sh b/tests/wamr-test-suites/test_wamr.sh index 54e2ac1a..8254cc71 100755 --- a/tests/wamr-test-suites/test_wamr.sh +++ b/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 From 67dce482018bc3103f02018d9682dcbb5154661f Mon Sep 17 00:00:00 2001 From: Wenyong Huang Date: Wed, 21 Aug 2024 12:55:34 +0800 Subject: [PATCH 2/5] Enable merged os_mmap for aot data sections and aot text (#3743) Enable merged os_mmap for aot data sections first, and try enabling merged os_mmap for them and aot text except on platform nuttx and esp-idf. This fixes the issue that aarch64 AOT module fails to load on android: https://github.com/bytecodealliance/wasm-micro-runtime/issues/2274 And also refine os_mmap related code. --- core/iwasm/aot/aot_loader.c | 243 ++++++++++++++++++++++++----------- core/iwasm/aot/aot_runtime.h | 7 + 2 files changed, 178 insertions(+), 72 deletions(-) diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 645f68b1..b96079d3 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/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 diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index d109ceba..0eb64798 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/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 From b00904b092ce478f7cfec71299db8a910a5a383a Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Thu, 22 Aug 2024 13:35:25 +0900 Subject: [PATCH 3/5] Add a comment on AOT_SECTION_TYPE_SIGNATURE (#3746) cf. https://github.com/bytecodealliance/wasm-micro-runtime/issues/3744 --- core/iwasm/aot/aot_runtime.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 0eb64798..76c78451 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -39,6 +39,10 @@ typedef enum AOTSectionType { AOT_SECTION_TYPE_FUNCTION = 3, AOT_SECTION_TYPE_EXPORT = 4, AOT_SECTION_TYPE_RELOCATION = 5, + /* + * Note: We haven't had anything to use AOT_SECTION_TYPE_SIGNATURE. + * It's just reserved for possible module signing features. + */ AOT_SECTION_TYPE_SIGNATURE = 6, AOT_SECTION_TYPE_CUSTOM = 100, } AOTSectionType; From e8c2952bf959f6df81972fc469a4357f04ee629a Mon Sep 17 00:00:00 2001 From: Anders Bakken Date: Thu, 22 Aug 2024 18:49:06 -0700 Subject: [PATCH 4/5] Fix arm64 issues on mac (#3688) Make wamrc normalize "arm64" to "aarch64v8". Previously the only way to make the "arm64" target was to not specify a target on 64 bit arm-based mac builds. Now arm64 and aarch64v8 are treated as the same. Make aot_loader accept "aarch64v8" on arm-based apple (as well as accepting legacy "arm64" based aot targets). This also removes __APPLE__ and __MACH__ from the block that defaults size_level to 1 since it doesn't seem to be supported for aarch64: `LLVM ERROR: Only small, tiny and large code models are allowed on AArch64` --- core/iwasm/aot/aot_loader.c | 4 ++++ core/iwasm/aot/arch/aot_reloc_aarch64.c | 11 ++--------- core/iwasm/compilation/aot_llvm.c | 9 +++++++++ wamr-compiler/main.c | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index b96079d3..e62e8278 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -579,6 +579,10 @@ load_target_info_section(const uint8 *buf, const uint8 *buf_end, return false; } + /* for backwards compatibility with previous wamrc aot files */ + if (!strcmp(target_info.arch, "arm64")) + bh_strcpy_s(target_info.arch, sizeof(target_info.arch), "aarch64v8"); + /* Check machine info */ if (!check_machine_info(&target_info, error_buf, error_buf_size)) { return false; diff --git a/core/iwasm/aot/arch/aot_reloc_aarch64.c b/core/iwasm/aot/arch/aot_reloc_aarch64.c index b4bb6024..ec646b4e 100644 --- a/core/iwasm/aot/arch/aot_reloc_aarch64.c +++ b/core/iwasm/aot/arch/aot_reloc_aarch64.c @@ -53,12 +53,6 @@ get_target_symbol_map(uint32 *sym_num) return target_sym_map; } -#if (defined(__APPLE__) || defined(__MACH__)) && defined(__arm64__) -#define BUILD_TARGET_AARCH64_DEFAULT "arm64" -#else -#define BUILD_TARGET_AARCH64_DEFAULT "aarch64v8" -#endif - void get_current_target(char *target_buf, uint32 target_buf_size) { @@ -68,8 +62,8 @@ get_current_target(char *target_buf, uint32 target_buf_size) /* Set to "aarch64v8" by default if sub version isn't specified */ if (strcmp(s, "AARCH64") == 0) { - s = BUILD_TARGET_AARCH64_DEFAULT; - s_size = sizeof(BUILD_TARGET_AARCH64_DEFAULT); + s = "aarch64v8"; + s_size = 9; /* strlen("aarch64v8"); */ } if (target_buf_size < s_size) { s_size = target_buf_size; @@ -83,7 +77,6 @@ get_current_target(char *target_buf, uint32 target_buf_size) /* Ensure the string is null byte ('\0') terminated */ *d = '\0'; } -#undef BUILD_TARGET_AARCH64_DEFAULT static uint32 get_plt_item_size() diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index d738cfc0..ab0b6ab0 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -2790,6 +2790,15 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) bh_assert(vendor_sys); bh_memcpy_s(default_arch, sizeof(default_arch), default_triple, (uint32)(vendor_sys - default_triple)); + /** + * On Mac M[1-9]+ LLVM will report arm64 as the + * architecture, for the purposes of wamr this is the + * same as aarch64v8 so we'll normalize it here. + */ + if (!strcmp(default_arch, "arm64")) { + bh_strcpy_s(default_arch, sizeof(default_arch), + "aarch64v8"); + } arch1 = default_arch; LLVMDisposeMessage(default_triple); diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index b3e731e5..8eed1c99 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -601,8 +601,8 @@ main(int argc, char *argv[]) LOG_VERBOSE("Set size level to 1 for Windows AOT file"); option.size_level = 1; } -#if defined(_WIN32) || defined(_WIN32_) || defined(__APPLE__) \ - || defined(__MACH__) +#if defined(_WIN32) || defined(_WIN32_) \ + || ((defined(__APPLE__) || defined(__MACH__)) && !defined(__arm64__)) if (!option.target_arch && !option.target_abi) { LOG_VERBOSE("Set size level to 1 for Windows or MacOS AOT file"); option.size_level = 1; From cb3a69f778d42444bb2c35698069b2bc1af96baa Mon Sep 17 00:00:00 2001 From: Huang Qi Date: Wed, 28 Aug 2024 16:05:07 +0800 Subject: [PATCH 5/5] CI: Freeze version of bloaty for NuttX compilation (#3756) Fix the compilation error of this CI: https://github.com/bytecodealliance/wasm-micro-runtime/actions/runs/10575515238 ``` /__w/wasm-micro-runtime/wasm-micro-runtime/bloaty/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc:139:32: error: no matching function for call to 'max(long int, int)' 139 | size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask; | ~~~~~~~~^~~~~~~~~~~~~~~~~ ``` --- .github/workflows/compilation_on_nuttx.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/compilation_on_nuttx.yml b/.github/workflows/compilation_on_nuttx.yml index 2f8014fa..627ada8e 100644 --- a/.github/workflows/compilation_on_nuttx.yml +++ b/.github/workflows/compilation_on_nuttx.yml @@ -124,6 +124,7 @@ jobs: repository: google/bloaty submodules: recursive path: bloaty + ref: 34f4a66559ad4938c1e629e9b5f54630b2b4d7b0 - name: Build Bloaty run: |