diff --git a/.github/workflows/compilation_on_hexagon.yml b/.github/workflows/compilation_on_hexagon.yml new file mode 100644 index 0000000000..d810916897 --- /dev/null +++ b/.github/workflows/compilation_on_hexagon.yml @@ -0,0 +1,227 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on hexagon (qemu user-mode) + +on: + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_hexagon.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_hexagon.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build_llvm_libraries: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "X86 Hexagon" + + build_wamrc: + needs: [build_llvm_libraries] + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ needs.build_llvm_libraries.outputs.cache_key }} + + - name: Quit if cache miss + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release --parallel $(nproc) + working-directory: wamr-compiler + + - name: Upload wamrc + uses: actions/upload-artifact@v7.0.1 + with: + name: wamrc-hexagon + path: wamr-compiler/build/wamrc + + build_iwasm: + runs-on: ubuntu-22.04 + strategy: + matrix: + include: + # fast-interp with SIMD (the product-mini/platforms/hexagon + # defaults); used by the fast-interp and aot spec test cells + - interp_variant: "fast-interp" + build_options: "" + # classic-interp; SIMD is disabled because SIMD in the + # interpreter requires fast-interp + - interp_variant: "classic-interp" + build_options: "-DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_SIMD=0" + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Install QEMU user-mode + run: sudo apt-get update && sudo apt-get install -y qemu-user + + - name: Install clang-22 and lld-22 + run: | + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-22 main" | sudo tee /etc/apt/sources.list.d/llvm-22.list + sudo apt-get update + sudo apt-get install -y clang-22 lld-22 + + - name: Install CodeLinaro Hexagon toolchain + run: | + wget -q https://artifacts.codelinaro.org/artifactory/codelinaro-toolchain-for-hexagon/22.1.4_/hexagon-debs-22.1.4_.tar.gz + mkdir hexagon-debs + tar xf hexagon-debs-22.1.4_.tar.gz -C hexagon-debs + sudo dpkg -i hexagon-debs/*.deb + + - name: Cross-compile iwasm for Hexagon + run: | + cmake -S product-mini/platforms/hexagon \ + -B build-hexagon \ + -DCMAKE_TOOLCHAIN_FILE=${GITHUB_WORKSPACE}/build-scripts/toolchains/hexagon_linux_musl.cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DWAMR_DISABLE_HW_BOUND_CHECK=1 \ + -DWAMR_BUILD_SHRUNK_MEMORY=0 \ + -DWAMR_BUILD_SPEC_TEST=1 \ + -DWAMR_BUILD_LIBC_WASI=0 \ + ${{ matrix.build_options }} + cmake --build build-hexagon --parallel $(nproc) + + - name: Verify iwasm binary + run: | + file build-hexagon/iwasm + qemu-hexagon -cpu v67 build-hexagon/iwasm --version + + - name: Upload iwasm + uses: actions/upload-artifact@v7.0.1 + with: + name: iwasm-hexagon-${{ matrix.interp_variant }} + path: build-hexagon/iwasm + + spec_test: + needs: [build_iwasm, build_wamrc] + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + test_option: + - "-s spec -b -P" + - "-s spec -b -S -P" + running_mode: + - "classic-interp" + - "fast-interp" + - "aot" + exclude: + # Known unsupported combination: SIMD in AOT mode is not yet + # validated on Hexagon. The Hexagon AOT relocation/codegen path for + # V128 has not been confirmed under qemu-hexagon, so this cell is + # deliberately excluded rather than left failing. SIMD is exercised + # in the fast-interp mode above; AOT is exercised for non-SIMD. + # Re-enable this cell once SIMD AOT is validated on hardware/QEMU. + - test_option: "-s spec -b -S -P" + running_mode: "aot" + # Unsupported build combination: SIMD in the interpreter requires + # fast-interp, so the classic-interp binary is built without SIMD. + - test_option: "-s spec -b -S -P" + running_mode: "classic-interp" + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Install QEMU user-mode + run: sudo apt-get update && sudo apt-get install -y qemu-user + + - name: Download iwasm artifact + uses: actions/download-artifact@v4.2.0 + with: + # the aot cell runs the fast-interp binary (both have AOT + # support; the interpreter kind does not matter for aot runs) + name: iwasm-hexagon-${{ matrix.running_mode == 'classic-interp' && 'classic-interp' || 'fast-interp' }} + path: ./ + + - name: Download wamrc artifact + if: matrix.running_mode == 'aot' + uses: actions/download-artifact@v4.2.0 + with: + name: wamrc-hexagon + path: ./ + + - name: Set up iwasm wrapper + run: | + chmod +x ./iwasm + # Place the real Hexagon binary at a known path + mv ./iwasm ./iwasm-hexagon-real + # Create a wrapper that invokes iwasm via qemu-hexagon user-mode. + # Use the linux platform path since Hexagon runs Linux and this + # avoids needing to teach every host-tool lookup about "hexagon". + mkdir -p product-mini/platforms/linux/build + printf '#!/bin/sh\nexec qemu-hexagon -cpu v67 %s/iwasm-hexagon-real "$@"\n' \ + "${GITHUB_WORKSPACE}" > product-mini/platforms/linux/build/iwasm + chmod +x product-mini/platforms/linux/build/iwasm + # Verify it works + product-mini/platforms/linux/build/iwasm --version + + - name: Set up wamrc + if: matrix.running_mode == 'aot' + run: | + chmod +x ./wamrc + mkdir -p wamr-compiler/build + cp ./wamrc wamr-compiler/build/wamrc + + - name: Run spec tests + timeout-minutes: 60 + run: | + ./test_wamr.sh ${{ matrix.test_option }} \ + -m HEXAGON \ + -t ${{ matrix.running_mode }} \ + -j linux \ + -Q \ + ${{ matrix.running_mode == 'aot' && format('-A {0}/wamr-compiler/build/wamrc', github.workspace) || '' }} + working-directory: ./tests/wamr-test-suites diff --git a/build-scripts/build_llvm.py b/build-scripts/build_llvm.py index e2221b8e54..d52afed404 100755 --- a/build-scripts/build_llvm.py +++ b/build-scripts/build_llvm.py @@ -243,6 +243,7 @@ def main(): "AArch64", "ARC", "ARM", + "Hexagon", "Mips", "RISCV", "WebAssembly", diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index ee00203b28..e9d9848e3e 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -45,6 +45,8 @@ elseif (WAMR_BUILD_TARGET STREQUAL "RISCV32_ILP32") add_definitions(-DBUILD_TARGET_RISCV32_ILP32) elseif (WAMR_BUILD_TARGET STREQUAL "ARC") add_definitions(-DBUILD_TARGET_ARC) +elseif (WAMR_BUILD_TARGET STREQUAL "HEXAGON") + add_definitions(-DBUILD_TARGET_HEXAGON) else () message (FATAL_ERROR "-- WAMR build target isn't set") endif () diff --git a/build-scripts/toolchains/hexagon_linux_musl.cmake b/build-scripts/toolchains/hexagon_linux_musl.cmake new file mode 100644 index 0000000000..06b5b2a977 --- /dev/null +++ b/build-scripts/toolchains/hexagon_linux_musl.cmake @@ -0,0 +1,22 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# CMake toolchain file for cross-compiling to Hexagon Linux (musl) +# Requires CodeLinaro hexagon-unknown-linux-musl toolchain and clang-22/lld-22. + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR hexagon) + +set(CMAKE_C_COMPILER hexagon-unknown-linux-musl-clang) +set(CMAKE_CXX_COMPILER hexagon-unknown-linux-musl-clang++) +set(CMAKE_ASM_COMPILER hexagon-unknown-linux-musl-clang) +set(CMAKE_AR llvm-ar-22) +set(CMAKE_RANLIB llvm-ranlib-22) + +set(CMAKE_C_FLAGS_INIT "-mv68 -G0") +set(CMAKE_CXX_FLAGS_INIT "-mv68 -G0") +set(CMAKE_EXE_LINKER_FLAGS_INIT "-static") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/core/config.h b/core/config.h index 31404deb95..6b2d8b6694 100644 --- a/core/config.h +++ b/core/config.h @@ -22,7 +22,8 @@ && !defined(BUILD_TARGET_RISCV32_ILP32D) \ && !defined(BUILD_TARGET_RISCV32_ILP32F) \ && !defined(BUILD_TARGET_RISCV32_ILP32) \ - && !defined(BUILD_TARGET_ARC) + && !defined(BUILD_TARGET_ARC) \ + && !defined(BUILD_TARGET_HEXAGON) /* clang-format on */ #if defined(__x86_64__) || defined(__x86_64) #define BUILD_TARGET_X86_64 @@ -52,6 +53,8 @@ #define BUILD_TARGET_RISCV32_ILP32D #elif defined(__arc__) #define BUILD_TARGET_ARC +#elif defined(__hexagon__) +#define BUILD_TARGET_HEXAGON #else #error "Build target isn't set" #endif @@ -276,6 +279,19 @@ #endif #endif +/* Whether the CPU supports unaligned SIMD/vector memory access. + * Some architectures have dedicated unaligned-load vector instructions, + * allowing V128 access at any alignment even when scalar loads require + * natural alignment. */ +#ifndef WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS +#if defined(BUILD_TARGET_X86_32) || defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_HEXAGON) +#define WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS 1 +#else +#define WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS 0 +#endif +#endif + /* WASM Interpreter labels-as-values feature */ #ifndef WASM_ENABLE_LABELS_AS_VALUES #ifdef __GNUC__ diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 121708d669..d7efed490b 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -275,6 +275,7 @@ GET_U16_FROM_ADDR(const uint8 *p) #define E_MACHINE_ARC_COMPACT 93 /* ARC International ARCompact */ #define E_MACHINE_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */ #define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define E_MACHINE_HEXAGON 164 /* Qualcomm Hexagon */ #define E_MACHINE_RISCV 243 /* RISC-V 32/64 */ #define E_MACHINE_WIN_I386 0x14c /* Windows i386 architecture */ #define E_MACHINE_WIN_X86_64 0x8664 /* Windows x86-64 architecture */ @@ -432,6 +433,9 @@ get_aot_file_target(AOTTargetInfo *target_info, char *target_buf, case E_MACHINE_ARC_COMPACT2: machine_type = "arc"; break; + case E_MACHINE_HEXAGON: + machine_type = "hexagon"; + break; default: set_error_buf_v(error_buf, error_buf_size, "unknown machine type %d", target_info->e_machine); diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index d1ec873c58..bae2016765 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -774,6 +774,7 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module, uint32 error_buf_size) { uint32 i, global_index, global_data_offset, base_offset, length; + uint32 cur_table_size; uint64 total_size; AOTTableInitData *table_seg; AOTTableInstance *tbl_inst = first_tbl_inst; @@ -827,10 +828,13 @@ tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module, #endif module_inst->tables[i] = tbl_inst; + /* keep each AOTTableInstance 8-byte aligned (64-bit members); + the allocation reserves this padding, see aot_instantiate() */ + cur_table_size = + (uint32)offsetof(AOTTableInstance, elems) + + (uint32)sizeof(table_elem_type_t) * tbl_inst->max_size; tbl_inst = (AOTTableInstance *)((uint8 *)tbl_inst - + offsetof(AOTTableInstance, elems) - + sizeof(table_elem_type_t) - * tbl_inst->max_size); + + align_uint(cur_table_size, 8)); } /* fill table with element segment content */ @@ -1926,24 +1930,31 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent, if (heap_size > APP_HEAP_SIZE_MAX) heap_size = APP_HEAP_SIZE_MAX; + /* Pad the global data so the table region starts 8-byte aligned */ total_size = (uint64)module_inst_struct_size + module_inst_mem_inst_size - + module->global_data_size; + + align_uint64(module->global_data_size, 8); /* - * calculate size of table data + * calculate size of table data. Each table instance size is rounded + * up to 8 bytes so every AOTTableInstance in the region is naturally + * aligned for its 64-bit members; keep this in sync with + * tables_instantiate(), get_tbl_inst_offset() in aot_emit_table.c + * and the table region derivation below. */ for (i = 0; i != module->import_table_count; ++i) { - table_size += offsetof(AOTTableInstance, elems); - table_size += (uint64)sizeof(table_elem_type_t) - * (uint64)aot_get_imp_tbl_data_slots( - module->import_tables + i, false); + uint64 cur_table_size = offsetof(AOTTableInstance, elems); + cur_table_size += (uint64)sizeof(table_elem_type_t) + * (uint64)aot_get_imp_tbl_data_slots( + module->import_tables + i, false); + table_size += align_uint64(cur_table_size, 8); } for (i = 0; i != module->table_count; ++i) { - table_size += offsetof(AOTTableInstance, elems); - table_size += + uint64 cur_table_size = offsetof(AOTTableInstance, elems); + cur_table_size += (uint64)sizeof(table_elem_type_t) * (uint64)aot_get_tbl_data_slots(module->tables + i, false); + table_size += align_uint64(cur_table_size, 8); } total_size += table_size; @@ -2065,8 +2076,10 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent, if (!global_instantiate(module_inst, module, error_buf, error_buf_size)) goto fail; - /* Initialize table info */ - p += module->global_data_size; + /* Initialize table info. Global data may end 4-byte aligned only; + pad so every AOTTableInstance is naturally aligned for its 64-bit + members (the size calculation above reserves this padding) */ + p += align_uint(module->global_data_size, 8); module_inst->table_count = module->table_count + module->import_table_count; if (!tables_instantiate(module_inst, module, (AOTTableInstance *)p, error_buf, error_buf_size)) diff --git a/core/iwasm/aot/arch/aot_reloc_hexagon.c b/core/iwasm/aot/arch/aot_reloc_hexagon.c new file mode 100644 index 0000000000..3fbce66858 --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_hexagon.c @@ -0,0 +1,752 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +/* + * Hexagon ELF relocation types. + * Reference: "Qualcomm Hexagon Application Binary Interface User Guide" + * https://docs.qualcomm.com/doc/80-N2040-23/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf + */ +#define R_HEX_NONE 0 +#define R_HEX_B22_PCREL 1 +#define R_HEX_B15_PCREL 2 +#define R_HEX_B7_PCREL 3 +#define R_HEX_LO16 4 +#define R_HEX_HI16 5 +#define R_HEX_32 6 +#define R_HEX_16 7 +#define R_HEX_8 8 +#define R_HEX_GPREL16_0 9 +#define R_HEX_GPREL16_1 10 +#define R_HEX_GPREL16_2 11 +#define R_HEX_GPREL16_3 12 +#define R_HEX_B13_PCREL 14 +#define R_HEX_B9_PCREL 15 +#define R_HEX_B32_PCREL_X 16 +#define R_HEX_32_6_X 17 +#define R_HEX_B22_PCREL_X 18 +#define R_HEX_B15_PCREL_X 19 +#define R_HEX_B13_PCREL_X 20 +#define R_HEX_B9_PCREL_X 21 +#define R_HEX_B7_PCREL_X 22 +#define R_HEX_16_X 23 +#define R_HEX_12_X 24 +#define R_HEX_11_X 25 +#define R_HEX_10_X 26 +#define R_HEX_9_X 27 +#define R_HEX_8_X 28 +#define R_HEX_7_X 29 +#define R_HEX_6_X 30 +#define R_HEX_32_PCREL 31 +#define R_HEX_6_PCREL_X 65 + +/* Instruction bit-field masks for relocations. */ +#define MASK_B22 0x01ff3ffe +#define MASK_B15 0x00df20fe +#define MASK_B13 0x00202ffe +#define MASK_B9 0x003000fe +#define MASK_B7 0x00001f18 +#define MASK_LO16 0x00c03fff +#define MASK_X26 0x0fff3fff + +/* Compiler runtime helpers required by AOT modules. */ +/* clang-format off */ +void __hexagon_divsi3(void); +void __hexagon_modsi3(void); +void __hexagon_udivsi3(void); +void __hexagon_umodsi3(void); +void __hexagon_divdi3(void); +void __hexagon_moddi3(void); +void __hexagon_udivdi3(void); +void __hexagon_umoddi3(void); +void __hexagon_udivmodsi4(void); +void __hexagon_udivmoddi4(void); + +void __hexagon_divsf3(void); +void __hexagon_divdf3(void); +void __hexagon_adddf3(void); +void __hexagon_subdf3(void); +void __hexagon_muldf3(void); +void __hexagon_sqrtf(void); +void __hexagon_sqrtdf2(void); +void __hexagon_fmadf4(void); +void __hexagon_fmadf5(void); + +void __hexagon_memcpy_likely_aligned_min32bytes_mult8bytes(void); +/* clang-format on */ + +/* clang-format off */ +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS + /* Integer division/modulo helpers */ + REG_SYM(__hexagon_divsi3), + REG_SYM(__hexagon_modsi3), + REG_SYM(__hexagon_udivsi3), + REG_SYM(__hexagon_umodsi3), + REG_SYM(__hexagon_divdi3), + REG_SYM(__hexagon_moddi3), + REG_SYM(__hexagon_udivdi3), + REG_SYM(__hexagon_umoddi3), + REG_SYM(__hexagon_udivmodsi4), + REG_SYM(__hexagon_udivmoddi4), + /* Floating-point helpers */ + REG_SYM(__hexagon_divsf3), + REG_SYM(__hexagon_divdf3), + REG_SYM(__hexagon_adddf3), + REG_SYM(__hexagon_subdf3), + REG_SYM(__hexagon_muldf3), + REG_SYM(__hexagon_sqrtf), + REG_SYM(__hexagon_sqrtdf2), + REG_SYM(__hexagon_fmadf4), + REG_SYM(__hexagon_fmadf5), + /* Optimized memory operations */ + REG_SYM(__hexagon_memcpy_likely_aligned_min32bytes_mult8bytes), +}; +/* clang-format on */ + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "hexagon"); +} + +#define INST_PARSE_PACKET_END 0x0000c000 + +static bool +is_duplex(uint32 insn) +{ + return (INST_PARSE_PACKET_END & insn) == 0; +} + +/* Instruction opcode → relocation mask table for R_HEX_6_X */ +/* clang-format off */ +static const struct { uint32 cmp_mask; uint32 reloc_mask; } r6_masks[] = { + { 0x38000000, 0x0000201f }, { 0x39000000, 0x0000201f }, + { 0x3e000000, 0x00001f80 }, { 0x3f000000, 0x00001f80 }, + { 0x40000000, 0x000020f8 }, { 0x41000000, 0x000007e0 }, + { 0x42000000, 0x000020f8 }, { 0x43000000, 0x000007e0 }, + { 0x44000000, 0x000020f8 }, { 0x45000000, 0x000007e0 }, + { 0x46000000, 0x000020f8 }, { 0x47000000, 0x000007e0 }, + { 0x6a000000, 0x00001f80 }, { 0x7c000000, 0x001f2000 }, + { 0x9a000000, 0x00000f60 }, { 0x9b000000, 0x00000f60 }, + { 0x9c000000, 0x00000f60 }, { 0x9d000000, 0x00000f60 }, + { 0x9f000000, 0x001f0100 }, { 0xab000000, 0x0000003f }, + { 0xad000000, 0x0000003f }, { 0xaf000000, 0x00030078 }, + { 0xd7000000, 0x006020e0 }, { 0xd8000000, 0x006020e0 }, + { 0xdb000000, 0x006020e0 }, { 0xdf000000, 0x006020e0 }, +}; +/* clang-format on */ + +static uint32 +get_mask_r6(uint32 insn, char *error_buf, uint32 error_buf_size) +{ + uint32 i; + + if (is_duplex(insn)) + return 0x03f00000; + + for (i = 0; i < sizeof(r6_masks) / sizeof(r6_masks[0]); i++) { + if ((insn & 0xff000000) == r6_masks[i].cmp_mask) + return r6_masks[i].reloc_mask; + } + + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "unrecognized instruction for 6_X relocation."); + return 0; +} + +static uint32 +get_mask_r8(uint32 insn) +{ + if ((0xff000000 & insn) == 0xde000000) + return 0x00e020e8; + if ((0xff000000 & insn) == 0x3c000000) + return 0x0000207f; + return 0x00001fe0; +} + +static uint32 +get_mask_r11(uint32 insn) +{ + if (is_duplex(insn)) + return 0x03f00000; + if ((0xff000000 & insn) == 0xa1000000) + return 0x060020ff; + return 0x06003fe0; +} + +static uint32 +get_mask_r16(uint32 insn, char *error_buf, uint32 error_buf_size) +{ + uint32 i; + + if (is_duplex(insn)) + return 0x03f00000; + + /* Clear end-packet-parse bits for matching */ + insn = insn & ~INST_PARSE_PACKET_END; + + if ((0xff000000 & insn) == 0x48000000) + return 0x061f20ff; + if ((0xff000000 & insn) == 0x49000000) + return 0x061f3fe0; + if ((0xff000000 & insn) == 0x78000000) + return 0x00df3fe0; + if ((0xff000000 & insn) == 0xb0000000) + return 0x0fe03fe0; + + if ((0xff802000 & insn) == 0x74000000) + return 0x00001fe0; + if ((0xff802000 & insn) == 0x74002000) + return 0x00001fe0; + if ((0xff802000 & insn) == 0x74800000) + return 0x00001fe0; + if ((0xff802000 & insn) == 0x74802000) + return 0x00001fe0; + + /* Fall back to r6 table */ + for (i = 0; i < sizeof(r6_masks) / sizeof(r6_masks[0]); i++) { + if ((insn & 0xff000000) == r6_masks[i].cmp_mask) + return r6_masks[i].reloc_mask; + } + + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "unrecognized instruction for 16_X relocation."); + return 0; +} + +/* Scatter bits from 'data' into positions indicated by set bits in 'mask'. */ +static uint32 +apply_mask(uint32 mask, uint32 data) +{ + uint32 result = 0; + uint32 off = 0; + uint32 bit; + + for (bit = 0; bit < 32; bit++) { + uint32 val_bit = (data >> off) & 1; + uint32 mask_bit = (mask >> bit) & 1; + if (mask_bit) { + result |= (val_bit << bit); + off++; + } + } + return result; +} + +#define PLT_ITEM_SIZE 12 + +/* Instruction templates with address = 0. + * The constant extender uses parse bits 0b01 (not-end-of-packet); 0b10 would + * mark the "{ immext; r28 = ##addr }" packet as :endloop0, so a trampoline + * entered while a hardware loop is active would decrement LC0 and branch to + * SA0 instead of jumping. The r28 transfer and the jumpr each end their + * packet (parse bits 0b11). These words match llvm-mc output exactly. */ +#define PLT_IMMEXT_TEMPLATE 0x00004000 +#define PLT_R28_TEMPLATE 0x7800c01c +#define PLT_JUMPR_R28 0x529cc000 + +#define MASK_R28_IMM 0x00df3fe0 + +uint32 +get_plt_item_size(void) +{ + return PLT_ITEM_SIZE; +} + +uint32 +get_plt_table_size(void) +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + + for (i = 0; i < num; i++) { + uint32 addr = (uint32)(uintptr_t)target_sym_map[i].symbol_addr; + uint32 *p = (uint32 *)plt; + + p[0] = PLT_IMMEXT_TEMPLATE | apply_mask(MASK_X26, addr >> 6); + p[1] = PLT_R28_TEMPLATE | apply_mask(MASK_R28_IMM, addr & 0x3F); + p[2] = PLT_JUMPR_R28; + + plt += PLT_ITEM_SIZE; + } +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_HEX_32: + { + /* Direct 32-bit relocation: S + A */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) = val; + break; + } + + case R_HEX_32_PCREL: + { + /* 32-bit PC-relative: S + A - P */ + int32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend + - (intptr_t)(target_section_addr + reloc_offset)); + *(int32 *)(target_section_addr + reloc_offset) = val; + break; + } + + case R_HEX_B22_PCREL: + { + /* + * 22-bit PC-relative branch: (S + A - P) >> 2 + * 22-bit signed field, word-aligned: +-8MB byte range. + * If the direct branch is out of range and the symbol has a + * PLT entry (symbol_index >= 0), fall back to a PLT trampoline, + * which can reach an arbitrary 32-bit absolute address. + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + if (result >= (8 * BH_MB) || result < -(8 * BH_MB)) { + /* Direct branch out of range: try PLT trampoline. The PLT + * entry is a fixed 3-instruction packet that jumps to the + * resolved symbol address, so a non-zero addend cannot be + * carried through it. */ + if (symbol_index < 0 || reloc_addend != 0) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "B22_PCREL target out of range."); + return false; + } + uint8 *plt = (uint8 *)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index; + result = (intptr_t)((uintptr_t)plt + - (uintptr_t)(target_section_addr + + reloc_offset)); + if (result >= (8 * BH_MB) || result < -(8 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "B22_PCREL PLT trampoline out of range."); + return false; + } + } + + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B22, (uint32)(result >> 2)); + break; + } + + case R_HEX_B15_PCREL: + { + /* 15-bit PC-relative branch: (S + A - P) >> 2, +-64KB range */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + if (result >= 0x10000 || result < -0x10000) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "B15_PCREL target out of range."); + return false; + } + + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B15, (uint32)(result >> 2)); + break; + } + + case R_HEX_B13_PCREL: + { + /* 13-bit PC-relative branch: (S + A - P) >> 2, +-16KB range */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + if (result >= 0x4000 || result < -0x4000) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "B13_PCREL target out of range."); + return false; + } + + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B13, (uint32)(result >> 2)); + break; + } + + case R_HEX_B9_PCREL: + { + /* 9-bit PC-relative branch: (S + A - P) >> 2, +-1KB range */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + if (result >= 0x400 || result < -0x400) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "B9_PCREL target out of range."); + return false; + } + + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B9, (uint32)(result >> 2)); + break; + } + + case R_HEX_B7_PCREL: + { + /* 7-bit PC-relative branch: (S + A - P) >> 2, +-256B range */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + if (result >= 0x100 || result < -0x100) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "B7_PCREL target out of range."); + return false; + } + + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B7, (uint32)(result >> 2)); + break; + } + + case R_HEX_LO16: + { + /* Low 16 bits of absolute address: (S + A) & 0xFFFF */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_LO16, val & 0xFFFF); + break; + } + + case R_HEX_HI16: + { + /* High 16 bits of absolute address: (S + A) >> 16 */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_LO16, val >> 16); + break; + } + + case R_HEX_B32_PCREL_X: + { + /* + * Extended 32-bit PC-relative for constant extender. + * Upper 26 bits: (S + A - P) >> 6 + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_X26, (uint32)(result >> 6)); + break; + } + + case R_HEX_32_6_X: + { + /* + * Extended 32-bit absolute for constant extender. + * Upper 26 bits: (S + A) >> 6 + */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_X26, val >> 6); + break; + } + + case R_HEX_B22_PCREL_X: + { + /* + * Extended 22-bit PC-relative: low 6 bits of (S + A - P). + * Paired with R_HEX_B32_PCREL_X on the constant extender. + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B22, (uint32)result & 0x3F); + break; + } + + case R_HEX_B15_PCREL_X: + { + /* + * Extended 15-bit PC-relative: low 6 bits of (S + A - P). + * Paired with R_HEX_B32_PCREL_X on the constant extender. + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B15, (uint32)result & 0x3F); + break; + } + + case R_HEX_B13_PCREL_X: + { + /* + * Extended 13-bit PC-relative: low 6 bits of (S + A - P). + * Paired with R_HEX_B32_PCREL_X on the constant extender. + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B13, (uint32)result & 0x3F); + break; + } + + case R_HEX_B9_PCREL_X: + { + /* + * Extended 9-bit PC-relative: low 6 bits of (S + A - P). + * Paired with R_HEX_B32_PCREL_X on the constant extender. + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B9, (uint32)result & 0x3F); + break; + } + + case R_HEX_B7_PCREL_X: + { + /* + * Extended 7-bit PC-relative: low 6 bits of (S + A - P). + * Paired with R_HEX_B32_PCREL_X on the constant extender. + */ + intptr_t result; + CHECK_RELOC_OFFSET(sizeof(uint32)); + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(MASK_B7, (uint32)result & 0x3F); + break; + } + + case R_HEX_6_X: + { + /* Low 6 bits for constant-extended absolute */ + uint32 val, insn, mask_r6; + CHECK_RELOC_OFFSET(sizeof(uint32)); + insn = *(uint32 *)(target_section_addr + reloc_offset); + mask_r6 = get_mask_r6(insn, error_buf, error_buf_size); + if (!mask_r6) + return false; + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(mask_r6, val & 0x3F); + break; + } + + case R_HEX_6_PCREL_X: + { + /* Low 6 bits for constant-extended PC-relative */ + intptr_t result; + uint32 insn, mask_r6; + CHECK_RELOC_OFFSET(sizeof(uint32)); + insn = *(uint32 *)(target_section_addr + reloc_offset); + mask_r6 = get_mask_r6(insn, error_buf, error_buf_size); + if (!mask_r6) + return false; + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(mask_r6, (uint32)result & 0x3F); + break; + } + + case R_HEX_16_X: + { + uint32 val, insn, mask_r16; + CHECK_RELOC_OFFSET(sizeof(uint32)); + insn = *(uint32 *)(target_section_addr + reloc_offset); + mask_r16 = get_mask_r16(insn, error_buf, error_buf_size); + if (!mask_r16) + return false; + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(mask_r16, val & 0x3F); + break; + } + + case R_HEX_12_X: + { + /* Extended 12-bit absolute: (S + A) with fixed mask */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(0x000007e0, val); + break; + } + + case R_HEX_11_X: + { + /* Extended 11-bit absolute: low 6 bits of (S + A) */ + uint32 val, insn; + CHECK_RELOC_OFFSET(sizeof(uint32)); + insn = *(uint32 *)(target_section_addr + reloc_offset); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(get_mask_r11(insn), val & 0x3F); + break; + } + + case R_HEX_10_X: + { + /* Extended 10-bit absolute: low 6 bits of (S + A) */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(0x00203fe0, val & 0x3F); + break; + } + + case R_HEX_9_X: + { + /* Extended 9-bit absolute: low 6 bits of (S + A) */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(0x00003fe0, val & 0x3F); + break; + } + + case R_HEX_8_X: + { + /* Extended 8-bit absolute: (S + A) */ + uint32 val, insn; + CHECK_RELOC_OFFSET(sizeof(uint32)); + insn = *(uint32 *)(target_section_addr + reloc_offset); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(get_mask_r8(insn), val); + break; + } + + case R_HEX_7_X: + { + /* Extended 7-bit absolute: low 6 bits of (S + A) go in the base + * instruction; the constant extender carries bits [31:6]. */ + uint32 val; + CHECK_RELOC_OFFSET(sizeof(uint32)); + val = (uint32)(uintptr_t)symbol_addr + (int32)reloc_addend; + *(uint32 *)(target_section_addr + reloc_offset) |= + apply_mask(0x00001f18, val & 0x3F); + break; + } + + case R_HEX_16: + { + /* Direct 16-bit relocation */ + uint16 val; + CHECK_RELOC_OFFSET(sizeof(uint16)); + val = (uint16)((uintptr_t)symbol_addr + (int32)reloc_addend); + *(uint16 *)(target_section_addr + reloc_offset) = val; + break; + } + + case R_HEX_8: + { + /* Direct 8-bit relocation: (S + A) truncated to 8 bits */ + uint8 val; + CHECK_RELOC_OFFSET(sizeof(uint8)); + val = (uint8)((uintptr_t)symbol_addr + (int32)reloc_addend); + *(uint8 *)(target_section_addr + reloc_offset) = val; + break; + } + + case R_HEX_NONE: + break; + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %" PRIu32 ".", + reloc_type); + return false; + } + + return true; +} diff --git a/core/iwasm/aot/iwasm_aot.cmake b/core/iwasm/aot/iwasm_aot.cmake index 1084681aca..b89173a41a 100644 --- a/core/iwasm/aot/iwasm_aot.cmake +++ b/core/iwasm/aot/iwasm_aot.cmake @@ -49,6 +49,8 @@ elseif (WAMR_BUILD_TARGET MATCHES "RISCV*") set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_riscv.c) elseif (WAMR_BUILD_TARGET STREQUAL "ARC") set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_arc.c) +elseif (WAMR_BUILD_TARGET STREQUAL "HEXAGON") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_hexagon.c) else () message (FATAL_ERROR "Build target isn't set") endif () diff --git a/core/iwasm/common/arch/invokeNative_hexagon.s b/core/iwasm/common/arch/invokeNative_hexagon.s new file mode 100644 index 0000000000..03f15efc0d --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_hexagon.s @@ -0,0 +1,142 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + .text + .align 2 + .globl invokeNative + .type invokeNative, @function + +/* + * void invokeNative(void (*native_code)(), uint32 argv[], uint32 argc) + * + * r0 = native_code, r1 = argv, r2 = argc + * Loads up to 6 words into r0-r5; remaining args are pushed to stack. + */ + +invokeNative: + { + allocframe(#16) + } + { + memd(r29+#0) = r17:16 + } + { + memd(r29+#8) = r19:18 + } + + // Save arguments to callee-saved registers. + // Also clear r0 so that if argc == 0 we don't call the native + // function with r0 still holding the function pointer. Reads + // happen before writes within a Hexagon packet, so r16 receives + // the original r0. + { + r16 = r0 // r16 = function_ptr + r17 = r1 // r17 = argv + r0 = #0 + } + { + r18 = r2 // r18 = argc + p0 = cmp.gt(r2, #0) + } + { + if (!p0) jump .Lcall // argc == 0: skip arg loading + } + + // Load register arguments from argv (up to 6 words in r0-r5) + { + r0 = memw(r17+#0) // r0 = argv[0] (exec_env) + p0 = cmp.gt(r18, #1) + } + { if (!p0) jump .Lcall } + + { + r1 = memw(r17+#4) // r1 = argv[1] + p0 = cmp.gt(r18, #2) + } + { if (!p0) jump .Lcall } + + { + r2 = memw(r17+#8) // r2 = argv[2] + p0 = cmp.gt(r18, #3) + } + { if (!p0) jump .Lcall } + + { + r3 = memw(r17+#12) // r3 = argv[3] + p0 = cmp.gt(r18, #4) + } + { if (!p0) jump .Lcall } + + { + r4 = memw(r17+#16) // r4 = argv[4] + p0 = cmp.gt(r18, #5) + } + { if (!p0) jump .Lcall } + + { + r5 = memw(r17+#20) // r5 = argv[5] + p0 = cmp.gt(r18, #6) + } + { if (!p0) jump .Lcall } + + // Stack arguments: argc > 6. + // Copy argv[6..argc-1] to the stack, maintaining 8-byte alignment. + { + r19 = add(r18, #-6) // r19 = number of stack args + } + { + r7 = asl(r19, #2) // r7 = stack_args * 4 (bytes) + } + { + r7 = add(r7, #7) // round up to 8-byte alignment + } + { + r7 = and(r7, #-8) + } + { + r29 = sub(r29, r7) // allocate aligned stack space + } + { + r6 = add(r17, #24) // r6 = &argv[6] (source) + r8 = r29 // r8 = stack destination + } + +.Lcopy_loop: + { + r9 = memw(r6++#4) // load next arg from argv + } + { + memw(r8++#4) = r9 // store to stack + r19 = add(r19, #-1) // decrement counter + } + { + p0 = cmp.gt(r19, #0) + if (p0.new) jump:t .Lcopy_loop + } + +.Lcall: + { + callr r16 // call native function + } + +.Lreturn: + { + r17:16 = memd(r30+#-16) + } + { + r19:18 = memd(r30+#-8) + } + { + deallocframe + } + { + jumpr r31 + } + + .size invokeNative, .-invokeNative + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/iwasm_common.cmake b/core/iwasm/common/iwasm_common.cmake index c3653f156c..7d51bbfbde 100644 --- a/core/iwasm/common/iwasm_common.cmake +++ b/core/iwasm/common/iwasm_common.cmake @@ -155,6 +155,8 @@ elseif (WAMR_BUILD_TARGET MATCHES "RISCV*") set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_riscv.S) elseif (WAMR_BUILD_TARGET STREQUAL "ARC") set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_arc.s) +elseif (WAMR_BUILD_TARGET STREQUAL "HEXAGON") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_hexagon.s) else () message (FATAL_ERROR "Build target isn't set") endif () diff --git a/core/iwasm/common/wasm_application.c b/core/iwasm/common/wasm_application.c index 7bbd43d166..2a58565e96 100644 --- a/core/iwasm/common/wasm_application.c +++ b/core/iwasm/common/wasm_application.c @@ -455,18 +455,19 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name, { float32 f32 = strtof(argv[i], &endptr); if (isnan(f32)) { -#ifdef _MSC_VER /* * Spec tests require the binary representation of NaN to be - * 0x7fc00000 for float and 0x7ff8000000000000 for float; - * however, in MSVC compiler, strtof doesn't return this - * exact value, causing some of the spec test failures. We - * use the value returned by nan/nanf as it is the one - * expected by spec tests. - * + * 0x7fc00000 for float and 0x7ff8000000000000 for double, + * however, some strtof implementations don't return this + * exact value: MSVC returns a different payload, and + * musl on Hexagon returns the hardware default NaN + * (0xffffffff, i.e. negative with an all-ones payload) + * because the parsed double is narrowed by a convert + * instruction that canonicalizes NaNs. Normalize to the + * value returned by nanf(""), then apply the requested + * sign and payload below. */ f32 = nanf(""); -#endif if (argv[i][0] == '-') { union ieee754_float u; u.f = f32; @@ -501,9 +502,9 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name, } u; u.val = strtod(argv[i], &endptr); if (isnan(u.val)) { -#ifdef _MSC_VER + /* normalize to the canonical quiet NaN, see the + VALUE_TYPE_F32 case above */ u.val = nan(""); -#endif if (argv[i][0] == '-') { union ieee754_double ud; ud.d = u.val; @@ -539,13 +540,22 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name, case VALUE_TYPE_V128: { /* it likes 0x123\0x234 or 123\234 */ + union { + uint64 val; + uint32 parts[2]; + } u; + /* argv1 + p is only guaranteed 4-byte aligned, so store + the two i64 halves word by word */ /* retrieve first i64 */ - *(uint64 *)(argv1 + p) = strtoull(argv[i], &endptr, 0); + u.val = strtoull(argv[i], &endptr, 0); + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; /* skip \ */ endptr++; /* retrieve second i64 */ - *(uint64 *)(argv1 + p + 2) = strtoull(endptr, &endptr, 0); - p += 4; + u.val = strtoull(endptr, &endptr, 0); + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; break; } #endif /* WASM_ENABLE_SIMD != 0 */ @@ -768,9 +778,18 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name, #if WASM_ENABLE_SIMD != 0 case VALUE_TYPE_V128: { - uint64 *v = (uint64 *)(argv1 + k); - os_printf("<0x%016" PRIx64 " 0x%016" PRIx64 ">:v128", *v, - *(v + 1)); + /* argv1 + k is only guaranteed 4-byte aligned, so read + the two i64 halves word by word */ + union { + uint64 val; + uint32 parts[2]; + } lo, hi; + lo.parts[0] = argv1[k]; + lo.parts[1] = argv1[k + 1]; + hi.parts[0] = argv1[k + 2]; + hi.parts[1] = argv1[k + 3]; + os_printf("<0x%016" PRIx64 " 0x%016" PRIx64 ">:v128", lo.val, + hi.val); k += 4; break; } diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 6590684c4f..72d037a0b0 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -5722,7 +5722,7 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, #if defined(BUILD_TARGET_X86_32) || defined(BUILD_TARGET_ARM) \ || defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_MIPS) \ - || defined(BUILD_TARGET_XTENSA) + || defined(BUILD_TARGET_XTENSA) || defined(BUILD_TARGET_HEXAGON) typedef void (*GenericFunctionPointer)(void); void invokeNative(GenericFunctionPointer f, uint32 *args, uint32 sz); @@ -5850,8 +5850,8 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, case VALUE_TYPE_I64: case VALUE_TYPE_F64: #if !defined(BUILD_TARGET_X86_32) - /* 64-bit data must be 8 bytes aligned in arm, thumb, mips - and xtensa */ + /* 64-bit data must be 8 bytes aligned in arm, thumb, mips, + xtensa and hexagon */ if (j & 1) j++; #endif diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index b6056eba66..f13fb6fca0 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -272,93 +272,10 @@ STORE_U16(void *addr, uint16_t value) ((uint8_t *)(addr))[1] = u.u8[1]; } -static inline void -STORE_V128(void *addr, V128 value) -{ - uintptr_t addr_ = (uintptr_t)(addr); - union { - V128 val; - uint64 u64[2]; - uint32 u32[4]; - uint16 u16[8]; - uint8 u8[16]; - } u; - - if ((addr_ & (uintptr_t)15) == 0) { - *(V128 *)addr = value; - } - else if ((addr_ & (uintptr_t)7) == 0) { - u.val = value; - ((uint64 *)(addr))[0] = u.u64[0]; - ((uint64 *)(addr))[1] = u.u64[1]; - } - else if ((addr_ & (uintptr_t)3) == 0) { - u.val = value; - ((uint32 *)addr)[0] = u.u32[0]; - ((uint32 *)addr)[1] = u.u32[1]; - ((uint32 *)addr)[2] = u.u32[2]; - ((uint32 *)addr)[3] = u.u32[3]; - } - else if ((addr_ & (uintptr_t)1) == 0) { - u.val = value; - ((uint16 *)addr)[0] = u.u16[0]; - ((uint16 *)addr)[1] = u.u16[1]; - ((uint16 *)addr)[2] = u.u16[2]; - ((uint16 *)addr)[3] = u.u16[3]; - ((uint16 *)addr)[4] = u.u16[4]; - ((uint16 *)addr)[5] = u.u16[5]; - ((uint16 *)addr)[6] = u.u16[6]; - ((uint16 *)addr)[7] = u.u16[7]; - } - else { - u.val = value; - for (int i = 0; i < 16; i++) - ((uint8 *)addr)[i] = u.u8[i]; - } -} +/* STORE_V128 / LOAD_V128 are defined separately below, guarded by + * WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS (see Block after line 474). */ /* For LOAD opcodes */ -static inline V128 -LOAD_V128(void *addr) -{ - uintptr_t addr1 = (uintptr_t)addr; - union { - V128 val; - uint64 u64[2]; - uint32 u32[4]; - uint16 u16[8]; - uint8 u8[16]; - } u; - if ((addr1 & (uintptr_t)15) == 0) - return *(V128 *)addr; - - if ((addr1 & (uintptr_t)7) == 0) { - u.u64[0] = ((uint64 *)addr)[0]; - u.u64[1] = ((uint64 *)addr)[1]; - } - else if ((addr1 & (uintptr_t)3) == 0) { - u.u32[0] = ((uint32 *)addr)[0]; - u.u32[1] = ((uint32 *)addr)[1]; - u.u32[2] = ((uint32 *)addr)[2]; - u.u32[3] = ((uint32 *)addr)[3]; - } - else if ((addr1 & (uintptr_t)1) == 0) { - u.u16[0] = ((uint16 *)addr)[0]; - u.u16[1] = ((uint16 *)addr)[1]; - u.u16[2] = ((uint16 *)addr)[2]; - u.u16[3] = ((uint16 *)addr)[3]; - u.u16[4] = ((uint16 *)addr)[4]; - u.u16[5] = ((uint16 *)addr)[5]; - u.u16[6] = ((uint16 *)addr)[6]; - u.u16[7] = ((uint16 *)addr)[7]; - } - else { - for (int i = 0; i < 16; i++) - u.u8[i] = ((uint8 *)addr)[i]; - } - return u.val; -} - static inline int64 LOAD_I64(void *addr) { @@ -473,6 +390,133 @@ LOAD_I16(void *addr) #endif /* WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 */ +/* + * LOAD_V128 / STORE_V128 — WASM linear memory V128 access. + * + * These are guarded by WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS rather than + * WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS because some architectures have + * different alignment rules for scalar vs vector memory operations. + * + * PUT_V128_TO_ADDR / GET_V128_FROM_ADDR (frame-local access) remain + * guarded by the scalar flag above since frame locals are accessed via + * scalar C operations, not vector instructions. + */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + +/* Already defined: LOAD_V128, STORE_V128 as direct pointer casts */ + +#elif WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS != 0 + +/* The target's SIMD unit supports unaligned vector access, but scalar loads + * require natural alignment. Use memcpy which is safe at any alignment and + * allows the compiler to select the best instruction sequence for the + * target. */ +static inline V128 +LOAD_V128(void *addr) +{ + V128 v; + memcpy(&v, addr, sizeof(V128)); + return v; +} + +static inline void +STORE_V128(void *addr, V128 value) +{ + memcpy(addr, &value, sizeof(V128)); +} + +#else /* !UNALIGNED_ADDR_ACCESS && !UNALIGNED_SIMD_ACCESS */ + +/* Neither scalar nor vector unaligned access is supported. + * Check alignment at runtime and use the widest safe access. */ +static inline void +STORE_V128(void *addr, V128 value) +{ + uintptr_t addr_ = (uintptr_t)(addr); + union { + V128 val; + uint64 u64[2]; + uint32 u32[4]; + uint16 u16[8]; + uint8 u8[16]; + } u; + + if ((addr_ & (uintptr_t)15) == 0) { + *(V128 *)addr = value; + } + else if ((addr_ & (uintptr_t)7) == 0) { + u.val = value; + ((uint64 *)(addr))[0] = u.u64[0]; + ((uint64 *)(addr))[1] = u.u64[1]; + } + else if ((addr_ & (uintptr_t)3) == 0) { + u.val = value; + ((uint32 *)addr)[0] = u.u32[0]; + ((uint32 *)addr)[1] = u.u32[1]; + ((uint32 *)addr)[2] = u.u32[2]; + ((uint32 *)addr)[3] = u.u32[3]; + } + else if ((addr_ & (uintptr_t)1) == 0) { + u.val = value; + ((uint16 *)addr)[0] = u.u16[0]; + ((uint16 *)addr)[1] = u.u16[1]; + ((uint16 *)addr)[2] = u.u16[2]; + ((uint16 *)addr)[3] = u.u16[3]; + ((uint16 *)addr)[4] = u.u16[4]; + ((uint16 *)addr)[5] = u.u16[5]; + ((uint16 *)addr)[6] = u.u16[6]; + ((uint16 *)addr)[7] = u.u16[7]; + } + else { + u.val = value; + for (int i = 0; i < 16; i++) + ((uint8 *)addr)[i] = u.u8[i]; + } +} + +static inline V128 +LOAD_V128(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + V128 val; + uint64 u64[2]; + uint32 u32[4]; + uint16 u16[8]; + uint8 u8[16]; + } u; + if ((addr1 & (uintptr_t)15) == 0) + return *(V128 *)addr; + + if ((addr1 & (uintptr_t)7) == 0) { + u.u64[0] = ((uint64 *)addr)[0]; + u.u64[1] = ((uint64 *)addr)[1]; + } + else if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32 *)addr)[0]; + u.u32[1] = ((uint32 *)addr)[1]; + u.u32[2] = ((uint32 *)addr)[2]; + u.u32[3] = ((uint32 *)addr)[3]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + u.u16[2] = ((uint16 *)addr)[2]; + u.u16[3] = ((uint16 *)addr)[3]; + u.u16[4] = ((uint16 *)addr)[4]; + u.u16[5] = ((uint16 *)addr)[5]; + u.u16[6] = ((uint16 *)addr)[6]; + u.u16[7] = ((uint16 *)addr)[7]; + } + else { + for (int i = 0; i < 16; i++) + u.u8[i] = ((uint8 *)addr)[i]; + } + return u.val; +} + +#endif /* WASM_CPU_SUPPORTS_UNALIGNED_SIMD_ACCESS */ + #if WASM_ENABLE_SHARED_MEMORY != 0 #define SHARED_MEMORY_LOCK(memory) shared_memory_lock(memory) #define SHARED_MEMORY_UNLOCK(memory) shared_memory_unlock(memory) diff --git a/core/iwasm/compilation/aot_emit_table.c b/core/iwasm/compilation/aot_emit_table.c index e8dae410bd..12fbb94b98 100644 --- a/core/iwasm/compilation/aot_emit_table.c +++ b/core/iwasm/compilation/aot_emit_table.c @@ -97,13 +97,19 @@ get_tbl_inst_offset(const AOTCompContext *comp_ctx, + (comp_ctx->pointer_size == sizeof(uint64) ? comp_ctx->comp_data->global_data_size_64bit : comp_ctx->comp_data->global_data_size_32bit); + /* global data is padded so the table region starts 8-byte aligned + and each table instance is padded to 8 bytes below, so every + AOTTableInstance stays naturally aligned for its 64-bit members; + keep in sync with the instance layout in aot_runtime.c/wasm_runtime.c */ + offset = align_uint64(offset, 8); while (i < tbl_idx && i < comp_ctx->comp_data->import_table_count) { - offset += offsetof(AOTTableInstance, elems); + uint64 tbl_inst_size = offsetof(AOTTableInstance, elems); /* avoid loading from current AOTTableInstance */ - offset += + tbl_inst_size += (uint64)comp_ctx->pointer_size * aot_get_imp_tbl_data_slots(imp_tbls + i, comp_ctx->is_jit_mode); + offset += align_uint64(tbl_inst_size, 8); ++i; } @@ -114,10 +120,12 @@ get_tbl_inst_offset(const AOTCompContext *comp_ctx, tbl_idx -= comp_ctx->comp_data->import_table_count; i -= comp_ctx->comp_data->import_table_count; while (i < tbl_idx && i < comp_ctx->comp_data->table_count) { - offset += offsetof(AOTTableInstance, elems); + uint64 tbl_inst_size = offsetof(AOTTableInstance, elems); /* avoid loading from current AOTTableInstance */ - offset += (uint64)comp_ctx->pointer_size - * aot_get_tbl_data_slots(tbls + i, comp_ctx->is_jit_mode); + tbl_inst_size += + (uint64)comp_ctx->pointer_size + * aot_get_tbl_data_slots(tbls + i, comp_ctx->is_jit_mode); + offset += align_uint64(tbl_inst_size, 8); ++i; } diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index e9f86817f0..8a06fb2990 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -191,7 +191,7 @@ aot_target_precheck_can_use_musttail(const AOTCompContext *comp_ctx) return false; } /* - * x86-64/i386: true + * x86-64/i386/hexagon: true * * others: assume true for now */ @@ -2286,7 +2286,8 @@ static ArchItem valid_archs[] = { { "thumbv8.1m.main", true }, { "riscv32", true }, { "riscv64", true }, - { "arc", true } + { "arc", true }, + { "hexagon", false } }; static const char *valid_abis[] = { @@ -2635,7 +2636,7 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) char *triple_norm_new = NULL, *cpu_new = NULL; char *err = NULL, *fp_round = "round.tonearest", *fp_exce = "fpexcept.strict"; - char triple_buf[128] = { 0 }, features_buf[128] = { 0 }; + char triple_buf[128] = { 0 }, features_buf[512] = { 0 }; uint32 opt_level, size_level, i; LLVMCodeModel code_model; LLVMTargetDataRef target_data_ref; @@ -3189,6 +3190,47 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) } #endif + /* Hexagon: disable small-data addressing; AOT code has no GP + * register set up so GP-relative relocations cannot be resolved. */ + { + bool is_hexagon = false; + if (arch && !strcmp(arch, "hexagon")) + is_hexagon = true; + else if (triple_norm && !strncmp(triple_norm, "hexagon", 7) + && (triple_norm[7] == '-' || triple_norm[7] == '\0')) + is_hexagon = true; + + if (is_hexagon) { + if (features[0] != '\0') { + /* Check for "-small-data" as a complete comma-delimited + * token to avoid false matches on "-small-data-limit=N". */ + const char *sd = features; + bool has_small_data = false; + while ((sd = strstr(sd, "-small-data")) != NULL) { + if ((sd == features || sd[-1] == ',') + && (sd[11] == '\0' || sd[11] == ',')) { + has_small_data = true; + break; + } + sd++; + } + if (!has_small_data) { + int ret = snprintf(features_buf, sizeof(features_buf), + "%s,-small-data", features); + if (ret < 0 || (size_t)ret >= sizeof(features_buf)) { + aot_set_last_error( + "Hexagon features string too long"); + goto fail; + } + features = features_buf; + } + } + else { + features = "-small-data"; + } + } + } + /* Get target with triple, note that LLVMGetTargetFromTriple() return 0 when success, but not true. */ if (LLVMGetTargetFromTriple(triple_norm, &target, &err) != 0) { @@ -3406,7 +3448,8 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) if (option->enable_simd && strcmp(comp_ctx->target_arch, "x86_64") != 0 && strncmp(comp_ctx->target_arch, "aarch64", 7) != 0 - && strcmp(comp_ctx->target_arch, "arc") != 0) { + && strcmp(comp_ctx->target_arch, "arc") != 0 + && strcmp(comp_ctx->target_arch, "hexagon") != 0) { /* Disable simd if it isn't supported by target arch */ option->enable_simd = false; } @@ -3433,6 +3476,21 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) } } + /* Determine whether the target's SIMD/vector unit handles unaligned + * access natively (so LLVMSetAlignment(load, 1) is safe and the backend + * will select the right unaligned instruction). Targets NOT listed here + * receive 1< (128b), + * not to HVX vectors (1024b). The HVX vmemu instruction is only selected + * for 1024b types, so align=1 on <2 x i64> produces byte-by-byte memub + * loads rather than unaligned HVX stores. Using 1<target_supports_unaligned_simd = + !strcmp(comp_ctx->target_arch, "x86_64") + || !strncmp(comp_ctx->target_arch, "aarch64", 7); + if (!(target_data_ref = LLVMCreateTargetDataLayout(comp_ctx->target_machine))) { aot_set_last_error("create LLVM target data layout failed."); diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index 5bd75a38ce..d89e776eb4 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -494,6 +494,11 @@ typedef struct AOTCompContext { bool enable_segue_f64_store; bool enable_segue_v128_store; + /* Whether the target's SIMD/vector unit supports unaligned access. + * When true, SIMD load/store IR can use align 1 without the backend + * decomposing to byte-by-byte access. */ + bool target_supports_unaligned_simd; + /* Whether optimize the JITed code */ bool optimize; diff --git a/core/iwasm/compilation/aot_llvm_extra.cpp b/core/iwasm/compilation/aot_llvm_extra.cpp index 3a3dc29a32..2a43a160cd 100644 --- a/core/iwasm/compilation/aot_llvm_extra.cpp +++ b/core/iwasm/compilation/aot_llvm_extra.cpp @@ -179,6 +179,9 @@ aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str) else if (targetArch == llvm::Triple::arc) { return true; } + else if (targetArch == llvm::Triple::hexagon) { + return true; + } else { return false; } diff --git a/core/iwasm/compilation/simd/simd_load_store.c b/core/iwasm/compilation/simd/simd_load_store.c index d3bbcc9650..fed2dc6cd6 100644 --- a/core/iwasm/compilation/simd/simd_load_store.c +++ b/core/iwasm/compilation/simd/simd_load_store.c @@ -17,9 +17,18 @@ simd_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, LLVMTypeRef data_type, bool enable_segue) { LLVMValueRef maddr, data; - - if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, - data_length, enable_segue, NULL))) { + unsigned int known_align; + + (void)align; + + /* The WASM alignment immediate (align) is only a hint and must NOT be + * trusted for codegen: a module may legally specify a large alignment + * while accessing an unaligned address. Use the alignment that + * aot_check_memory_overflow can actually prove from the effective + * address instead (1 unless statically known to be more aligned). */ + if (!(maddr = + aot_check_memory_overflow(comp_ctx, func_ctx, offset, data_length, + enable_segue, &known_align))) { HANDLE_FAILURE("aot_check_memory_overflow"); return NULL; } @@ -35,7 +44,15 @@ simd_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, return NULL; } - LLVMSetAlignment(data, 1); + /* On targets whose SIMD unit natively handles misaligned access (e.g. + * x86_64, aarch64), align=1 lets the backend pick the unaligned vector + * instruction directly. On targets where it does not (e.g. Hexagon), + * align=1 expands to a byte-by-byte scalar sequence; use the proven + * alignment so the backend can select a wider load (e.g. memd on Hexagon) + * when the address is statically known to be aligned, while still being + * correct (no fault) for unaligned accesses. */ + LLVMSetAlignment( + data, comp_ctx->target_supports_unaligned_simd ? 1 : known_align); return data; } @@ -285,9 +302,13 @@ simd_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, LLVMTypeRef value_ptr_type, bool enable_segue) { LLVMValueRef maddr, result; + unsigned int known_align; + + (void)align; - if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, - data_length, enable_segue, NULL))) + if (!(maddr = + aot_check_memory_overflow(comp_ctx, func_ctx, offset, data_length, + enable_segue, &known_align))) return false; if (!(maddr = LLVMBuildBitCast(comp_ctx->builder, maddr, value_ptr_type, @@ -301,7 +322,10 @@ simd_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, return false; } - LLVMSetAlignment(result, 1); + /* See simd_load for the alignment rationale: use the proven alignment, + * never the untrusted WASM alignment hint. */ + LLVMSetAlignment( + result, comp_ctx->target_supports_unaligned_simd ? 1 : known_align); return true; } diff --git a/core/iwasm/fast-jit/jit_frontend.c b/core/iwasm/fast-jit/jit_frontend.c index c96b5410ba..9bbb00c8e3 100644 --- a/core/iwasm/fast-jit/jit_frontend.c +++ b/core/iwasm/fast-jit/jit_frontend.c @@ -44,7 +44,10 @@ get_global_base_offset(const WASMModule *module) static uint32 get_first_table_inst_offset(const WASMModule *module) { - return get_global_base_offset(module) + module->global_data_size; + /* global data is padded so the table region starts 8-byte aligned, + keep in sync with wasm_instantiate() */ + return get_global_base_offset(module) + + align_uint(module->global_data_size, 8); } uint32 @@ -73,16 +76,20 @@ jit_frontend_get_table_inst_offset(const WASMModule *module, uint32 tbl_idx) while (i < tbl_idx && i < module->import_table_count) { WASMTableImport *import_table = &module->import_tables[i].u.table; + uint32 tbl_inst_size = (uint32)offsetof(WASMTableInstance, elems); - offset += (uint32)offsetof(WASMTableInstance, elems); #if WASM_ENABLE_MULTI_MODULE != 0 - offset += (uint32)sizeof(uint32) * import_table->table_type.max_size; + tbl_inst_size += (uint32)sizeof(table_elem_type_t) + * import_table->table_type.max_size; #else - offset += (uint32)sizeof(uint32) - * (import_table->table_type.possible_grow - ? import_table->table_type.max_size - : import_table->table_type.init_size); + tbl_inst_size += (uint32)sizeof(table_elem_type_t) + * (import_table->table_type.possible_grow + ? import_table->table_type.max_size + : import_table->table_type.init_size); #endif + /* each table instance is padded to 8 bytes, keep in sync with + wasm_instantiate() */ + offset += align_uint(tbl_inst_size, 8); i++; } @@ -95,17 +102,20 @@ jit_frontend_get_table_inst_offset(const WASMModule *module, uint32 tbl_idx) i -= module->import_table_count; while (i < tbl_idx && i < module->table_count) { WASMTable *table = module->tables + i; + uint32 tbl_inst_size = (uint32)offsetof(WASMTableInstance, elems); - offset += (uint32)offsetof(WASMTableInstance, elems); #if WASM_ENABLE_MULTI_MODULE != 0 - offset += + tbl_inst_size += (uint32)sizeof(table_elem_type_t) * table->table_type.max_size; #else - offset += + tbl_inst_size += (uint32)sizeof(table_elem_type_t) * (table->table_type.possible_grow ? table->table_type.max_size : table->table_type.init_size); #endif + /* each table instance is padded to 8 bytes, keep in sync with + wasm_instantiate() */ + offset += align_uint(tbl_inst_size, 8); i++; } diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index abde189ebc..3501758595 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -4621,16 +4621,19 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, addr = POP_MEM_OFFSET(); CHECK_MEMORY_OVERFLOW(8); + /* maddr points into linear memory and may have any + alignment (the memarg align field is only a hint), so + use the alignment-safe STORE_I64 rather than + PUT_I64_TO_ADDR, which requires a 4-byte aligned + destination */ #if WASM_ENABLE_MEMORY64 != 0 if (is_memory64) { - PUT_I64_TO_ADDR((mem_offset_t *)maddr, - GET_I64_FROM_ADDR(frame_sp + 2)); + STORE_I64(maddr, GET_I64_FROM_ADDR(frame_sp + 2)); } else #endif { - PUT_I64_TO_ADDR((uint32 *)maddr, - GET_I64_FROM_ADDR(frame_sp + 1)); + STORE_I64(maddr, GET_I64_FROM_ADDR(frame_sp + 1)); } CHECK_WRITE_WATCHPOINT(addr, offset); HANDLE_OP_END(); diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 937a7fdecf..9cfb1de952 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -6001,7 +6001,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, frame_ip += sizeof(V128); addr_ret = GET_OFFSET(); - PUT_V128_TO_ADDR(frame_lp + addr_ret, *(V128 *)orig_ip); + /* the immediate lives in the compiled code stream, + which is not guaranteed V128-aligned */ + PUT_V128_TO_ADDR(frame_lp + addr_ret, + LOAD_V128(orig_ip)); break; } /* TODO: Add a faster SIMD implementation */ @@ -6522,17 +6525,26 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, break; } -#define SIMD_LOAD_LANE_COMMON(vec, register, lane, width) \ - do { \ - addr_ret = GET_OFFSET(); \ - CHECK_MEMORY_OVERFLOW(width / 8); \ - if (width == 64) { \ - vec.register[lane] = GET_I64_FROM_ADDR((uint32 *)maddr); \ - } \ - else { \ - vec.register[lane] = *(uint##width *)(maddr); \ - } \ - PUT_V128_TO_ADDR(frame_lp + addr_ret, vec); \ +/* maddr points into linear memory and may have any alignment (the + memarg align field is only a hint), so use the alignment-safe + LOAD_* helpers rather than direct dereferences */ +#define SIMD_LOAD_LANE_COMMON(vec, register, lane, width) \ + do { \ + addr_ret = GET_OFFSET(); \ + CHECK_MEMORY_OVERFLOW(width / 8); \ + if (width == 64) { \ + vec.register[lane] = LOAD_I64(maddr); \ + } \ + else if (width == 32) { \ + vec.register[lane] = LOAD_U32(maddr); \ + } \ + else if (width == 16) { \ + vec.register[lane] = LOAD_U16(maddr); \ + } \ + else { \ + vec.register[lane] = *(uint8 *)(maddr); \ + } \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, vec); \ } while (0) #define SIMD_LOAD_LANE_OP(register, width) \ @@ -6566,21 +6578,30 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, SIMD_LOAD_LANE_OP(i64x2, 64); break; } -#define SIMD_STORE_LANE_OP(register, width) \ - do { \ - uint32 offset, addr; \ - offset = read_uint32(frame_ip); \ - V128 vec = POP_V128(); \ - addr = POP_I32(); \ - int lane = *frame_ip++; \ - SIMD_LANE_HANDLE_UNALIGNED_ACCESS(); \ - CHECK_MEMORY_OVERFLOW(width / 8); \ - if (width == 64) { \ - STORE_I64(maddr, vec.register[lane]); \ - } \ - else { \ - *(uint##width *)(maddr) = vec.register[lane]; \ - } \ +/* maddr points into linear memory and may have any alignment (the + memarg align field is only a hint), so use the alignment-safe + STORE_* helpers rather than direct dereferences */ +#define SIMD_STORE_LANE_OP(register, width) \ + do { \ + uint32 offset, addr; \ + offset = read_uint32(frame_ip); \ + V128 vec = POP_V128(); \ + addr = POP_I32(); \ + int lane = *frame_ip++; \ + SIMD_LANE_HANDLE_UNALIGNED_ACCESS(); \ + CHECK_MEMORY_OVERFLOW(width / 8); \ + if (width == 64) { \ + STORE_I64(maddr, vec.register[lane]); \ + } \ + else if (width == 32) { \ + STORE_U32(maddr, (uint32)vec.register[lane]); \ + } \ + else if (width == 16) { \ + STORE_U16(maddr, (uint16)vec.register[lane]); \ + } \ + else { \ + *(uint8 *)(maddr) = (uint8)(vec.register[lane]); \ + } \ } while (0) case SIMD_v128_store8_lane: diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index a2c67bea2c..110e79be33 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -10564,16 +10564,19 @@ reserve_block_ret(WASMLoaderContext *loader_ctx, uint8 opcode, uint8 *emit_data = NULL, *cells = NULL; int16 *src_offsets = NULL; uint16 *dst_offsets = NULL; - uint64 size = - (uint64)value_count - * (sizeof(*cells) + sizeof(*src_offsets) + sizeof(*dst_offsets)); + /* Pad the cells array to 2 bytes so the int16/uint16 arrays that + follow it stay naturally aligned (required on strict-alignment + targets) */ + uint64 size = (uint64)align_uint(value_count, 2) * sizeof(*cells) + + (uint64)value_count + * (sizeof(*src_offsets) + sizeof(*dst_offsets)); /* Allocate memory for the emit data */ if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) return false; cells = emit_data; - src_offsets = (int16 *)(cells + value_count); + src_offsets = (int16 *)(cells + align_uint(value_count, 2)); dst_offsets = (uint16 *)(src_offsets + value_count); /* insert op_copy before else opcode */ @@ -11472,19 +11475,20 @@ copy_params_to_dynamic_space(WASMLoaderContext *loader_ctx, char *error_buf, bool is_if_block = (block->label_type == LABEL_TYPE_IF ? true : false); int16 operand_offset = 0; - uint64 size = (uint64)param_count * (sizeof(*cells) + sizeof(*src_offsets)); - bh_assert(size > 0); - /* For if block, we also need copy the condition operand offset. */ - if (is_if_block) - size += sizeof(*cells) + sizeof(*src_offsets); + uint32 cell_count = is_if_block ? param_count + 1 : param_count; + /* Pad the cells array to 2 bytes so the int16 array that follows it + stays naturally aligned (required on strict-alignment targets) */ + uint64 size = (uint64)align_uint(cell_count, 2) * sizeof(*cells) + + (uint64)cell_count * sizeof(*src_offsets); + bh_assert(size > 0); /* Allocate memory for the emit data */ if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) return false; cells = emit_data; - src_offsets = (int16 *)(cells + param_count); + src_offsets = (int16 *)(cells + align_uint(cell_count, 2)); if (is_if_block) condition_offset = *loader_ctx->frame_offset; @@ -13385,13 +13389,13 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, int32 offset = (int32)((uint8 *)handle_table[opcode_tmp] - (uint8 *)handle_table[0]); - *(int32 *)(p_code_compiled_tmp - - sizeof(int32)) = offset; + STORE_U32(p_code_compiled_tmp - sizeof(int32), + (uint32)offset); #else /* emit uint32 label address in 32-bit target */ - *(uint32 *)(p_code_compiled_tmp - - sizeof(uint32)) = - (uint32)(uintptr_t)handle_table[opcode_tmp]; + STORE_U32(p_code_compiled_tmp - sizeof(uint32), + (uint32)(uintptr_t) + handle_table[opcode_tmp]); #endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ #if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 @@ -13421,13 +13425,13 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, int32 offset = (int32)((uint8 *)handle_table[opcode_tmp] - (uint8 *)handle_table[0]); - *(int32 *)(p_code_compiled_tmp - - sizeof(int32)) = offset; + STORE_U32(p_code_compiled_tmp - sizeof(int32), + (uint32)offset); #else /* emit uint32 label address in 32-bit target */ - *(uint32 *)(p_code_compiled_tmp - - sizeof(uint32)) = - (uint32)(uintptr_t)handle_table[opcode_tmp]; + STORE_U32(p_code_compiled_tmp - sizeof(uint32), + (uint32)(uintptr_t) + handle_table[opcode_tmp]); #endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ #if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 @@ -13558,12 +13562,12 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, /* emit int32 relative offset in 64-bit target */ int32 offset = (int32)((uint8 *)handle_table[opcode_tmp] - (uint8 *)handle_table[0]); - *(int32 *)(p_code_compiled_tmp - sizeof(int32)) = - offset; + STORE_U32(p_code_compiled_tmp - sizeof(int32), + (uint32)offset); #else /* emit uint32 label address in 32-bit target */ - *(uint32 *)(p_code_compiled_tmp - sizeof(uint32)) = - (uint32)(uintptr_t)handle_table[opcode_tmp]; + STORE_U32(p_code_compiled_tmp - sizeof(uint32), + (uint32)(uintptr_t)handle_table[opcode_tmp]); #endif #endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 879df37c89..ad1a3d49ea 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -702,7 +702,10 @@ tables_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, table->max_size = max_size_fixed; } - table = (WASMTableInstance *)((uint8 *)table + (uint32)total_size); + /* keep each WASMTableInstance 8-byte aligned (64-bit members); + the allocation reserves this padding, see wasm_instantiate() */ + table = (WASMTableInstance *)((uint8 *)table + + align_uint((uint32)total_size, 8)); #if WASM_ENABLE_MULTI_MODULE != 0 table_linked++; #endif @@ -747,7 +750,10 @@ tables_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, table->cur_size = module->tables[i].table_type.init_size; table->max_size = max_size_fixed; - table = (WASMTableInstance *)((uint8 *)table + (uint32)total_size); + /* keep each WASMTableInstance 8-byte aligned (64-bit members); + the allocation reserves this padding, see wasm_instantiate() */ + table = (WASMTableInstance *)((uint8 *)table + + align_uint((uint32)total_size, 8)); } bh_assert(table_index == table_count); @@ -2467,33 +2473,41 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, total_size = (uint64)module_inst_struct_size + module_inst_mem_inst_size + module->global_data_size; - /* Calculate the size of table data */ + /* Calculate the size of table data. Each table instance size is + rounded up to 8 bytes so every WASMTableInstance in the region is + naturally aligned for its 64-bit members; keep this in sync with + tables_instantiate() and the first_table derivation. */ for (i = 0; i < module->import_table_count; i++) { WASMTableImport *import_table = &module->import_tables[i].u.table; - table_size += offsetof(WASMTableInstance, elems); + uint64 cur_table_size = offsetof(WASMTableInstance, elems); #if WASM_ENABLE_MULTI_MODULE != 0 - table_size += (uint64)sizeof(table_elem_type_t) - * import_table->table_type.max_size; + cur_table_size += (uint64)sizeof(table_elem_type_t) + * import_table->table_type.max_size; #else - table_size += (uint64)sizeof(table_elem_type_t) - * (import_table->table_type.possible_grow - ? import_table->table_type.max_size - : import_table->table_type.init_size); + cur_table_size += (uint64)sizeof(table_elem_type_t) + * (import_table->table_type.possible_grow + ? import_table->table_type.max_size + : import_table->table_type.init_size); #endif + table_size += align_uint64(cur_table_size, 8); } for (i = 0; i < module->table_count; i++) { WASMTable *table = module->tables + i; - table_size += offsetof(WASMTableInstance, elems); + uint64 cur_table_size = offsetof(WASMTableInstance, elems); #if WASM_ENABLE_MULTI_MODULE != 0 - table_size += + cur_table_size += (uint64)sizeof(table_elem_type_t) * table->table_type.max_size; #else - table_size += + cur_table_size += (uint64)sizeof(table_elem_type_t) * (table->table_type.possible_grow ? table->table_type.max_size : table->table_type.init_size); #endif + table_size += align_uint64(cur_table_size, 8); } + /* Pad the global data so the table region starts 8-byte aligned */ + total_size += align_uint64(module->global_data_size, 8) + - module->global_data_size; total_size += table_size; /* The offset of WASMModuleInstanceExtra, make it 8-byte aligned */ @@ -2607,8 +2621,12 @@ wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, module_inst->global_data = (uint8 *)module_inst + module_inst_struct_size + module_inst_mem_inst_size; module_inst->global_data_size = module->global_data_size; - first_table = (WASMTableInstance *)(module_inst->global_data - + module->global_data_size); + /* global data may end 4-byte aligned only; pad so every + WASMTableInstance is naturally aligned for its 64-bit members + (the size calculation above reserves this padding) */ + first_table = + (WASMTableInstance *)(module_inst->global_data + + align_uint64(module->global_data_size, 8)); module_inst->memory_count = module->import_memory_count + module->memory_count; diff --git a/doc/build_wamr.md b/doc/build_wamr.md index cd931a4662..61dcd8b406 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -132,7 +132,7 @@ Above compilation flags map to macros in `config.h`. For example, `WAMR_BUILD_AO - **WAMR_BUILD_PLATFORM**: set the target platform. Match the platform folder name under [core/shared/platform](../core/shared/platform). -- **WAMR_BUILD_TARGET**: set the target CPU architecture. Supported targets: X86_64, X86_32, AARCH64, ARM, THUMB, XTENSA, ARC, RISCV32, RISCV64, and MIPS. +- **WAMR_BUILD_TARGET**: set the target CPU architecture. Supported targets: X86_64, X86_32, AARCH64, ARM, THUMB, XTENSA, ARC, RISCV32, RISCV64, MIPS, and HEXAGON. - For ARM and THUMB, use `[][_VFP]`. `` is the ARM sub-architecture. `_VFP` means arguments and returns use VFP coprocessor registers s0-s15 (d0-d7). Both are optional, for example ARMV7, ARMV7_VFP, THUMBV7, or THUMBV7_VFP. - For AARCH64, use `[]`. VFP is on by default. `` is optional, for example AARCH64, AARCH64V8, or AARCH64V8.1. - For RISCV64, use `[_abi]`. `_abi` is optional. Supported: RISCV64, RISCV64_LP64D, and RISCV64_LP64. RISCV64 and RISCV64_LP64D both use [LP64D](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (LP64 with hardware floating-point for FLEN=64). RISCV64_LP64 uses [LP64](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (integer calling convention only; no hardware floating-point calling convention). diff --git a/product-mini/platforms/hexagon/CMakeLists.txt b/product-mini/platforms/hexagon/CMakeLists.txt new file mode 100644 index 0000000000..dc2c46a161 --- /dev/null +++ b/product-mini/platforms/hexagon/CMakeLists.txt @@ -0,0 +1,144 @@ +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +project (iwasm) + +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) + +set (CMAKE_VERBOSE_MAKEFILE OFF) + +# Hexagon uses the Linux kernel +set (WAMR_BUILD_PLATFORM "linux") +set (WAMR_BUILD_TARGET "HEXAGON") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (CMAKE_C_STANDARD 99) +set (CMAKE_CXX_STANDARD 17) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Fast JIT not supported on Hexagon + set (WAMR_BUILD_FAST_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif() + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + set (WAMR_BUILD_REF_TYPES 1) +endif () + +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +check_pie_supported() + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +# Share main.c with Linux platform since Hexagon runs Linux kernel +add_executable (iwasm + ${CMAKE_CURRENT_SOURCE_DIR}/../linux/main.c + ${UNCOMMON_SHARED_SOURCE} +) + +set_version_info (iwasm) + +target_link_libraries(iwasm vmlib) + +install (TARGETS iwasm DESTINATION bin) + +add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (vmlib) + +target_include_directories(vmlib INTERFACE + $ +) + +set (WAMR_PUBLIC_HEADERS + ${WAMR_ROOT_DIR}/core/iwasm/include/wasm_c_api.h + ${WAMR_ROOT_DIR}/core/iwasm/include/wasm_export.h + ${WAMR_ROOT_DIR}/core/iwasm/include/lib_export.h +) + +set_target_properties (vmlib PROPERTIES + OUTPUT_NAME iwasm + PUBLIC_HEADER "${WAMR_PUBLIC_HEADERS}" +) + +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -lpthread) + +install (TARGETS vmlib + EXPORT iwasmTargets + DESTINATION lib + PUBLIC_HEADER DESTINATION include +) + +install_iwasm_package () diff --git a/tests/wamr-test-suites/spec-test-script/all.py b/tests/wamr-test-suites/spec-test-script/all.py index 970127d0b2..bbc917a9fc 100644 --- a/tests/wamr-test-suites/spec-test-script/all.py +++ b/tests/wamr-test-suites/spec-test-script/all.py @@ -58,6 +58,7 @@ def get_iwasm_cmd(platform: str) -> str: "AARCH64_VFP", "ARMV7", "ARMV7_VFP", + "HEXAGON", "RISCV32", "RISCV32_ILP32F", "RISCV32_ILP32D", @@ -95,6 +96,36 @@ def ignore_the_case( if "i386" == target and case_name in ["float_exprs", "conversions"]: return True + # Hexagon-specific failures. These skips are workarounds for + # hardware/toolchain/libc traits, not WAMR bugs: + # - float_exprs: Hexagon FP arithmetic produces the default NaN + # (all-ones payload) instead of the wasm-canonical quiet NaN, so + # the *_nan_bitpattern checks fail (same class as the x87 skip for + # i386 above). + # - simd_f32x4_pmin_pmax / simd_f64x2_pmin_pmax: clang Hexagon backend + # miscompiles the strict-IEEE pattern "(b < a) ? b : a" into + # sfmin/sfmax (minNum NaN semantics) without nnan, so pmin/pmax with + # a NaN first operand return the wrong lane. + # - simd_lane / simd_splat: musl (LD64) strtod parses long hex-float + # CLI arguments 1 ulp low (e.g. 0x0123456789ABCDEFabcdef), a test + # harness artifact, not an engine bug. + # - i32 / i64: clang miscompiles variable-count rotl/rotr on Hexagon + # (llvm.fshl/fshr lowered without reducing the count mod the width; + # present at least since clang 18). Fixed by llvm-project + # 006429da6ab9. Re-enabling needs the fix in BOTH the Hexagon cross + # toolchain that builds iwasm (interp modes) AND the LLVM that wamrc + # links (aot mode) -- verified: interp passes with a fixed 22.1.8 + # toolchain, and aot fails with any unfixed LLVM (18.1.8 .. 22.x). + # (f32_bitwise and conversions were skipped here until the NaN + # CLI-argument normalization fix in wasm_application.c; both pass + # now.) + if "hexagon" == target and case_name in [ + "float_exprs", "i32", "i64", + "simd_f32x4_pmin_pmax", "simd_f64x2_pmin_pmax", + "simd_lane", "simd_splat", + ]: + return True + # esp32s3 qemu doesn't have PSRAM emulation if qemu_flag and target == 'xtensa' and case_name in ["memory_size"]: return True @@ -186,9 +217,13 @@ def test_case( CMD.append("--interpreter") if sgx_flag: CMD.append(IWASM_SGX_CMD) - elif qemu_flag: + elif qemu_flag and qemu_firmware: + # System-emulation (e.g. NuttX): iwasm is a built-in command inside + # the emulated OS, so use the bare name. CMD.append(IWASM_QEMU_CMD) else: + # Host execution or QEMU user-mode: use the host path to the iwasm + # binary (which may be a qemu-hexagon wrapper). CMD.append(IWASM_CMD) if no_pty: CMD.append("--no-pty") @@ -220,9 +255,19 @@ def test_case( CMD.append("--eh") if qemu_flag: - CMD.append("--qemu") - CMD.append("--qemu-firmware") - CMD.append(qemu_firmware) + if qemu_firmware: + CMD.append("--qemu") + CMD.append("--qemu-firmware") + CMD.append(qemu_firmware) + # Increase timeouts for QEMU emulation. Defaults: 30s start, 20s + # test, 2s start-fail. Under qemu user-mode (e.g. qemu-hexagon), + # both startup and trap-on-startup can take >>2s, so bump all three. + CMD.append("--start-timeout") + CMD.append("120") + CMD.append("--test-timeout") + CMD.append("120") + CMD.append("--start-fail-timeout") + CMD.append("30") if not clean_up_flag: CMD.append("--no_cleanup") @@ -598,9 +643,20 @@ def main(): ) parser.add_argument('--no-pty', action='store_true', help="Use direct pipes instead of pseudo-tty") + parser.add_argument( + "--interpreter", + default="", + dest="interpreter", + help="Specify the iwasm interpreter path (overrides the default)", + ) options = parser.parse_args() + # Override global IWASM_CMD if --interpreter is specified + global IWASM_CMD + if options.interpreter: + IWASM_CMD = options.interpreter + # Convert target to lower case for internal use, e.g. X86_64 -> x86_64 # target is always exist, so no need to check it options.target = options.target.lower() diff --git a/tests/wamr-test-suites/spec-test-script/runtest.py b/tests/wamr-test-suites/spec-test-script/runtest.py index fa9f5eb7da..d50bc8de38 100755 --- a/tests/wamr-test-suites/spec-test-script/runtest.py +++ b/tests/wamr-test-suites/spec-test-script/runtest.py @@ -62,6 +62,7 @@ "riscv64_lp64f": ["--target=riscv64", "--target-abi=lp64f", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c,+f", "--size-level=1"], "riscv64_lp64d": ["--target=riscv64", "--target-abi=lp64d", "--cpu=generic-rv64", "--cpu-features=+m,+a,+c,+f,+d", "--size-level=1"], "xtensa": ["--target=xtensa"], + "hexagon": ["--target=hexagon"], } # AOT compilation options mapping for XIP mode @@ -267,7 +268,7 @@ def assert_prompt(runner, prompts, timeout, is_need_execute_result): log("Started with:\n%s" % header) else: log("Did not one of following prompt(s): %s" % repr(prompts)) - log(" Got : %s" % repr(r.buf)) + log(" Got : %s" % repr(runner.buf)) raise Exception("Did not one of following prompt(s)") @@ -780,6 +781,8 @@ def is_result_match_expected(out, expected): def test_assert(r, opts, mode, cmd, expected): log("Testing(%s) %s = %s" % (mode, cmd, expected)) out = invoke(r, opts, cmd) + if out is None: + raise Exception("Timed out waiting for response to: %s" % cmd) if '\n' in out or ' ' in out: outs = [''] + out.split('\n')[1:] out = outs[-1] @@ -821,34 +824,34 @@ def test_assert_return(r, opts, form): n. to search a pattern like (assert_return (invoke $module_name function_name ... ) ...) """ # params, return - m = re.search(r'^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S) + m = re.search(r'^\(assert_return\s*\(invoke\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S) # judge if assert_return cmd includes the module name - n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S) + n = re.search(r'^\(assert_return\s*\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s+(\(.*\))\s*\)\s*(\(.*\))\s*\)\s*$', form, re.S) # print("assert_return with {}".format(form)) if not m: # no params, return - m = re.search(r'^\(assert_return\s+\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S) + m = re.search(r'^\(assert_return\s*\(invoke\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S) if not m: # params, no return - m = re.search(r'^\(assert_return\s+\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S) + m = re.search(r'^\(assert_return\s*\(invoke\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S) if not m: # no params, no return - m = re.search(r'^\(assert_return\s+\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S) + m = re.search(r'^\(assert_return\s*\(invoke\s+"([^"]*)"\s*()()\)\s*\)\s*$', form, re.S) if not m: # params, return if not n: # no params, return - n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S) + n = re.search(r'^\(assert_return\s*\(invoke\s+\$((?:[^\s])*)\s+"((?:[^"]|\\\")*)"\s*\)\s+()(\(.*\))\s*\)\s*$', form, re.S) if not n: # params, no return - n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S) + n = re.search(r'^\(assert_return\s*\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"\s+(\(.*\))()\s*\)\s*\)\s*$', form, re.S) if not n: # no params, no return - n = re.search(r'^\(assert_return\s+\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"*()()\)\s*\)\s*$', form, re.S) + n = re.search(r'^\(assert_return\s*\(invoke\s+\$((?:[^\s])*)\s+"([^"]*)"*()()\)\s*\)\s*$', form, re.S) if not m and not n: - if re.search(r'^\(assert_return\s+\(get.*\).*\)$', form, re.S): + if re.search(r'^\(assert_return\s*\(get.*\).*\)$', form, re.S): log("ignoring assert_return get") return else: diff --git a/tests/wamr-test-suites/test_wamr.sh b/tests/wamr-test-suites/test_wamr.sh index 97dc84d548..eae0c5934d 100755 --- a/tests/wamr-test-suites/test_wamr.sh +++ b/tests/wamr-test-suites/test_wamr.sh @@ -634,7 +634,9 @@ function spec_test() if [[ ${ENABLE_QEMU} == 1 ]]; then ARGS_FOR_SPEC_TEST+="--qemu " - ARGS_FOR_SPEC_TEST+="--qemu-firmware ${QEMU_FIRMWARE} " + if [[ -n ${QEMU_FIRMWARE} ]]; then + ARGS_FOR_SPEC_TEST+="--qemu-firmware ${QEMU_FIRMWARE} " + fi fi if [[ ${PLATFORM} == "windows" ]]; then