diff --git a/.github/workflows/run-tests-aarch64-be.yml b/.github/workflows/run-tests-aarch64-be.yml new file mode 100644 index 000000000..656030155 --- /dev/null +++ b/.github/workflows/run-tests-aarch64-be.yml @@ -0,0 +1,80 @@ +# +# Copyright 2026 Zetier Inc +# +# This file is part of VOLK +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +name: Run VOLK tests on big-endian AArch64 + +on: [push, pull_request] + +env: + # Ubuntu has no big-endian AArch64 cross toolchain, so use Arm's. + ARM_TOOLCHAIN: arm-gnu-toolchain-15.3.rel2-x86_64-aarch64_be-none-linux-gnu + ARM_TOOLCHAIN_URL: https://gitlab.arm.com/api/v4/projects/tooling%2Fgnu-toolchains-for-arm/packages/generic/gnu-toolchain/15.3.rel2 + ARM_TOOLCHAIN_SHA256: bdf8e9b4c62791c5c3aeeb8ba3791421d73c7d147dd04f0da502716885bcc1d2 + +jobs: + Tests: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + with: + submodules: "recursive" + - name: Install packages + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update -q -y + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y python3-mako cmake qemu-user curl + - name: Install sccache + shell: bash + run: sudo scripts/ci/install_sccache.bash "aarch64_be tests" + continue-on-error: true + - name: Cache toolchain + id: cache_toolchain + uses: actions/cache@v5 + with: + path: ~/${{ env.ARM_TOOLCHAIN }} + key: ${{ env.ARM_TOOLCHAIN }} + - name: Download toolchain + if: steps.cache_toolchain.outputs.cache-hit != 'true' + run: | + cd ~ + curl -fsSL -o "$ARM_TOOLCHAIN.tar.xz" "$ARM_TOOLCHAIN_URL/$ARM_TOOLCHAIN.tar.xz" + echo "$ARM_TOOLCHAIN_SHA256 $ARM_TOOLCHAIN.tar.xz" | sha256sum -c - + tar -xJf "$ARM_TOOLCHAIN.tar.xz" + rm "$ARM_TOOLCHAIN.tar.xz" + - name: Add toolchain to PATH + run: echo "$HOME/$ARM_TOOLCHAIN/bin" >> "$GITHUB_PATH" + - name: Prepare sccache usage + id: enable_sccache + run: scripts/ci/setup_cmake_cache_flags.bash "aarch64_be tests" + - name: Configure + run: > + cmake + -S . + -B build + -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/aarch64_be-none-linux-gnu.cmake + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_C_FLAGS="-Werror" + -DCMAKE_CXX_FLAGS="-Werror" + -DBUILD_EXECUTABLE=ON + ${{ steps.enable_sccache.outputs.CXX_LAUNCHER }} + ${{ steps.enable_sccache.outputs.C_LAUNCHER }} + - name: Build + run: cmake --build build -j$(nproc) + - name: Print info + run: | + sysroot=$(aarch64_be-none-linux-gnu-gcc -print-sysroot) + for opt in --alignment --avail-machines --all-machines --malloc --cc; do + qemu-aarch64_be -L "$sysroot" -E LD_LIBRARY_PATH="$(dirname "$sysroot")/lib64" \ + build/apps/volk-config-info $opt + done + - name: Test + run: cd build && ctest -V + - name: Show final sccache and build directory stats + if: always() + continue-on-error: true + shell: bash + run: scripts/ci/build-summary.sh "aarch64_be tests" "./build/" diff --git a/cmake/Toolchains/aarch64_be-none-linux-gnu.cmake b/cmake/Toolchains/aarch64_be-none-linux-gnu.cmake new file mode 100644 index 000000000..a79fee9ff --- /dev/null +++ b/cmake/Toolchains/aarch64_be-none-linux-gnu.cmake @@ -0,0 +1,45 @@ +# +# Copyright 2026 Zetier Inc +# +# This file is part of VOLK +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +# Big-endian AArch64 with the Arm GNU Toolchain (aarch64_be-none-linux-gnu), +# whose bin directory must be on the PATH. Tests run under qemu-aarch64_be. + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64_be) + +set(TOOLCHAIN_PREFIX aarch64_be-none-linux-gnu-) + +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) +set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) + +# The toolchain ships its own glibc sysroot; libstdc++ and libgcc_s sit +# outside it, next to the sysroot in the toolchain's target directory. +execute_process( + COMMAND ${CMAKE_C_COMPILER} -print-sysroot + RESULT_VARIABLE TOOLCHAIN_SYSROOT_RESULT + OUTPUT_VARIABLE TOOLCHAIN_SYSROOT + OUTPUT_STRIP_TRAILING_WHITESPACE) +if(NOT TOOLCHAIN_SYSROOT_RESULT EQUAL 0 OR TOOLCHAIN_SYSROOT STREQUAL "") + message(FATAL_ERROR "Could not run ${CMAKE_C_COMPILER} -print-sysroot; " + "put the toolchain's bin directory on the PATH") +endif() +get_filename_component(TOOLCHAIN_SYSROOT "${TOOLCHAIN_SYSROOT}" REALPATH) +get_filename_component(TOOLCHAIN_TARGET_DIR "${TOOLCHAIN_SYSROOT}" DIRECTORY) + +set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_SYSROOT}) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# One string, as VolkAddTest pastes it into each test's shell script. The -E +# replaces the scripts' LD_LIBRARY_PATH; the build tree's RPATH still finds +# libvolk. +set(CMAKE_CROSSCOMPILING_EMULATOR + "qemu-aarch64_be -L ${TOOLCHAIN_SYSROOT} -E LD_LIBRARY_PATH=${TOOLCHAIN_TARGET_DIR}/lib64") diff --git a/kernels/volk/volk_32f_index_max_16u.h b/kernels/volk/volk_32f_index_max_16u.h index f788ded77..16fe6d673 100644 --- a/kernels/volk/volk_32f_index_max_16u.h +++ b/kernels/volk/volk_32f_index_max_16u.h @@ -277,7 +277,10 @@ volk_32f_index_max_16u_neon(uint16_t* target, const float* src0, uint32_t num_po const float* inputPtr = src0; // Use integer indices directly - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_max = vdupq_n_f32(-FLT_MAX); @@ -345,7 +348,10 @@ volk_32f_index_max_16u_neonv8(uint16_t* target, const float* src0, uint32_t num_ const float* inputPtr = src0; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_max = vdupq_n_f32(-FLT_MAX); diff --git a/kernels/volk/volk_32f_index_max_32u.h b/kernels/volk/volk_32f_index_max_32u.h index 2d7cd8057..784142dc5 100644 --- a/kernels/volk/volk_32f_index_max_32u.h +++ b/kernels/volk/volk_32f_index_max_32u.h @@ -333,7 +333,10 @@ volk_32f_index_max_32u_neonv8(uint32_t* target, const float* src0, uint32_t num_ const float* inputPtr = src0; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_max = vdupq_n_f32(-FLT_MAX); diff --git a/kernels/volk/volk_32f_index_min_16u.h b/kernels/volk/volk_32f_index_min_16u.h index 3911f51d0..1e426750a 100644 --- a/kernels/volk/volk_32f_index_min_16u.h +++ b/kernels/volk/volk_32f_index_min_16u.h @@ -269,7 +269,10 @@ volk_32f_index_min_16u_neon(uint16_t* target, const float* source, uint32_t num_ const float* inputPtr = source; // Use integer indices directly - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_min = vdupq_n_f32(FLT_MAX); @@ -337,7 +340,10 @@ volk_32f_index_min_16u_neonv8(uint16_t* target, const float* source, uint32_t nu const float* inputPtr = source; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_min = vdupq_n_f32(FLT_MAX); diff --git a/kernels/volk/volk_32f_index_min_32u.h b/kernels/volk/volk_32f_index_min_32u.h index 38644c1db..14fe76130 100644 --- a/kernels/volk/volk_32f_index_min_32u.h +++ b/kernels/volk/volk_32f_index_min_32u.h @@ -316,7 +316,10 @@ volk_32f_index_min_32u_neonv8(uint32_t* target, const float* source, uint32_t nu const float* inputPtr = source; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_min = vdupq_n_f32(FLT_MAX); diff --git a/kernels/volk/volk_32fc_conjugate_32fc.h b/kernels/volk/volk_32fc_conjugate_32fc.h index 507adf2d6..6fe18cf8e 100644 --- a/kernels/volk/volk_32fc_conjugate_32fc.h +++ b/kernels/volk/volk_32fc_conjugate_32fc.h @@ -271,9 +271,10 @@ static inline void volk_32fc_conjugate_32fc_neonv8(lv_32fc_t* cVector, lv_32fc_t* c = cVector; const lv_32fc_t* a = aVector; - /* Sign mask to flip imaginary parts: [0, -0, 0, -0] */ - const uint32x4_t sign_mask = - vreinterpretq_u32_f32((float32x4_t){ 0.0f, -0.0f, 0.0f, -0.0f }); + /* Sign mask to flip imaginary parts: [0, -0, 0, -0], loaded from memory + * because a brace-initialized vector is lane-reversed on big-endian GCC */ + static const float sign_bits[4] = { 0.0f, -0.0f, 0.0f, -0.0f }; + const uint32x4_t sign_mask = vreinterpretq_u32_f32(vld1q_f32(sign_bits)); /* Process 4 complex numbers per iteration (2x unroll) */ while (n >= 4) { diff --git a/kernels/volk/volk_32fc_index_max_16u.h b/kernels/volk/volk_32fc_index_max_16u.h index 514065fcf..7fa45b76f 100644 --- a/kernels/volk/volk_32fc_index_max_16u.h +++ b/kernels/volk/volk_32fc_index_max_16u.h @@ -327,7 +327,10 @@ volk_32fc_index_max_16u_neon(uint16_t* target, const lv_32fc_t* src0, uint32_t n const lv_32fc_t* inputPtr = src0; // Use integer indices directly - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_max = vdupq_n_f32(0.0f); @@ -404,7 +407,10 @@ static inline void volk_32fc_index_max_16u_neonv8(uint16_t* target, const lv_32fc_t* inputPtr = src0; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_max = vdupq_n_f32(0.0f); diff --git a/kernels/volk/volk_32fc_index_max_32u.h b/kernels/volk/volk_32fc_index_max_32u.h index b97fe6e32..fbb492c3d 100644 --- a/kernels/volk/volk_32fc_index_max_32u.h +++ b/kernels/volk/volk_32fc_index_max_32u.h @@ -609,7 +609,10 @@ static inline void volk_32fc_index_max_32u_neonv8(uint32_t* target, const lv_32fc_t* inputPtr = src0; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_max = vdupq_n_f32(0.0f); diff --git a/kernels/volk/volk_32fc_index_min_16u.h b/kernels/volk/volk_32fc_index_min_16u.h index 8aa12bde4..6afd72eb9 100644 --- a/kernels/volk/volk_32fc_index_min_16u.h +++ b/kernels/volk/volk_32fc_index_min_16u.h @@ -326,7 +326,10 @@ static inline void volk_32fc_index_min_16u_neon(uint16_t* target, const lv_32fc_t* inputPtr = source; // Use integer indices directly - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_min = vdupq_n_f32(FLT_MAX); @@ -403,7 +406,10 @@ static inline void volk_32fc_index_min_16u_neonv8(uint16_t* target, const lv_32fc_t* inputPtr = source; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_min = vdupq_n_f32(FLT_MAX); diff --git a/kernels/volk/volk_32fc_index_min_32u.h b/kernels/volk/volk_32fc_index_min_32u.h index b2fce7c54..f28c7ac1c 100644 --- a/kernels/volk/volk_32fc_index_min_32u.h +++ b/kernels/volk/volk_32fc_index_min_32u.h @@ -605,7 +605,10 @@ static inline void volk_32fc_index_min_32u_neonv8(uint32_t* target, const lv_32fc_t* inputPtr = source; // Use integer indices directly (no float conversion overhead) - uint32x4_t vec_indices = { 0, 1, 2, 3 }; + // Load from an array to avoid GCC filling a brace-initialized vector like + // { 0, 1, 2, 3 } in reverse lane order on big-endian targets + static const uint32_t init_indices[4] = { 0, 1, 2, 3 }; + uint32x4_t vec_indices = vld1q_u32(init_indices); const uint32x4_t vec_incr = vdupq_n_u32(4); float32x4_t vec_min = vdupq_n_f32(FLT_MAX); diff --git a/kernels/volk/volk_32u_byteswap.h b/kernels/volk/volk_32u_byteswap.h index d5d0613ec..6b6a19bd3 100644 --- a/kernels/volk/volk_32u_byteswap.h +++ b/kernels/volk/volk_32u_byteswap.h @@ -201,18 +201,17 @@ static inline void volk_32u_byteswap_neonv8(uint32_t* intsToSwap, unsigned int n uint32_t* inputPtr = (uint32_t*)intsToSwap; const unsigned int n8points = num_points / 8; uint8x16_t input; - uint8x16_t idx = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12 }; unsigned int number = 0; for (number = 0; number < n8points; ++number) { __VOLK_PREFETCH(inputPtr + 8); input = vld1q_u8((uint8_t*)inputPtr); - input = vqtbl1q_u8(input, idx); + input = vrev32q_u8(input); vst1q_u8((uint8_t*)inputPtr, input); inputPtr += 4; input = vld1q_u8((uint8_t*)inputPtr); - input = vqtbl1q_u8(input, idx); + input = vrev32q_u8(input); vst1q_u8((uint8_t*)inputPtr, input); inputPtr += 4; } diff --git a/kernels/volk/volk_32u_reverse_32u.h b/kernels/volk/volk_32u_reverse_32u.h index 3be939165..6b10458b3 100644 --- a/kernels/volk/volk_32u_reverse_32u.h +++ b/kernels/volk/volk_32u_reverse_32u.h @@ -262,15 +262,13 @@ volk_32u_reverse_32u_neonv8(uint32_t* out, const uint32_t* in, unsigned int num_ const uint32_t* in_ptr = in; uint32_t* out_ptr = out; - const uint8x16_t idx = { 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12 }; - const unsigned int quarterPoints = num_points / 4; unsigned int number = 0; for (; number < quarterPoints; ++number) { __VOLK_PREFETCH(in_ptr + 4); uint32x4_t x = vld1q_u32(in_ptr); uint32x4_t z = - vreinterpretq_u32_u8(vqtbl1q_u8(vrbitq_u8(vreinterpretq_u8_u32(x)), idx)); + vreinterpretq_u32_u8(vrev32q_u8(vrbitq_u8(vreinterpretq_u8_u32(x)))); vst1q_u32(out_ptr, z); in_ptr += 4; out_ptr += 4; diff --git a/kernels/volk/volk_8u_x4_conv_k7_r2_8u.h b/kernels/volk/volk_8u_x4_conv_k7_r2_8u.h index cb2db11ac..2989aff06 100644 --- a/kernels/volk/volk_8u_x4_conv_k7_r2_8u.h +++ b/kernels/volk/volk_8u_x4_conv_k7_r2_8u.h @@ -307,6 +307,17 @@ static inline void volk_8u_x4_conv_k7_r2_8u_spiral(unsigned char* Y, #include +// Vector pointer casts move lanes in reverse NEON order on big-endian GCC, so +// use vld1q/vst1q there. On little-endian keep the casts: unlike vld1 they let +// 32-bit ARM use offset addressing for the loads and stores below. +#ifdef __ARM_BIG_ENDIAN +#define NEONSPIRAL_LOAD(p) vld1q_u8(p) +#define NEONSPIRAL_STORE(p, v) vst1q_u8((p), (v)) +#else +#define NEONSPIRAL_LOAD(p) (*(uint8x16_t*)(p)) +#define NEONSPIRAL_STORE(p, v) (*(uint8x16_t*)(p) = (v)) +#endif + static inline void volk_8u_x4_conv_k7_r2_8u_neonspiral(unsigned char* Y, unsigned char* X, unsigned char* syms, @@ -329,13 +340,13 @@ static inline void volk_8u_x4_conv_k7_r2_8u_neonspiral(unsigned char* Y, uint8x8x2_t both; // First half of butterfly - s18 = ((uint8x16_t*)X)[0]; - s19 = ((uint8x16_t*)X)[2]; + s18 = NEONSPIRAL_LOAD(X); + s19 = NEONSPIRAL_LOAD(X + 2 * 16); a76 = vdupq_n_u8(syms[2 * i]); - a78 = ((uint8x16_t*)Branchtab)[0]; + a78 = NEONSPIRAL_LOAD(Branchtab); a79 = veorq_u8(a76, a78); a82 = vdupq_n_u8(syms[2 * i + 1]); - a84 = ((uint8x16_t*)Branchtab)[2]; + a84 = NEONSPIRAL_LOAD(Branchtab + 2 * 16); a85 = veorq_u8(a82, a84); a86 = vrhaddq_u8(a79, a85); t14 = vshrq_n_u8(a86, 2); @@ -365,18 +376,18 @@ static inline void volk_8u_x4_conv_k7_r2_8u_neonspiral(unsigned char* Y, left = vget_low_u8(a89); right = vget_low_u8(a90); both = vzip_u8(left, right); - ((uint8x16_t*)Y)[0] = vcombine_u8(both.val[0], both.val[1]); + NEONSPIRAL_STORE(Y, vcombine_u8(both.val[0], both.val[1])); left = vget_high_u8(a89); right = vget_high_u8(a90); both = vzip_u8(left, right); - ((uint8x16_t*)Y)[1] = vcombine_u8(both.val[0], both.val[1]); + NEONSPIRAL_STORE(Y + 1 * 16, vcombine_u8(both.val[0], both.val[1])); // Second half of butterfly - s24 = ((uint8x16_t*)X)[1]; - s25 = ((uint8x16_t*)X)[3]; - a100 = ((uint8x16_t*)Branchtab)[1]; + s24 = NEONSPIRAL_LOAD(X + 1 * 16); + s25 = NEONSPIRAL_LOAD(X + 3 * 16); + a100 = NEONSPIRAL_LOAD(Branchtab + 1 * 16); a101 = veorq_u8(a76, a100); - a103 = ((uint8x16_t*)Branchtab)[3]; + a103 = NEONSPIRAL_LOAD(Branchtab + 3 * 16); a104 = veorq_u8(a82, a103); a105 = vrhaddq_u8(a101, a104); t17 = vshrq_n_u8(a105, 2); @@ -406,28 +417,28 @@ static inline void volk_8u_x4_conv_k7_r2_8u_neonspiral(unsigned char* Y, left = vget_low_u8(a108); right = vget_low_u8(a109); both = vzip_u8(left, right); - ((uint8x16_t*)Y)[2] = vcombine_u8(both.val[0], both.val[1]); + NEONSPIRAL_STORE(Y + 2 * 16, vcombine_u8(both.val[0], both.val[1])); left = vget_high_u8(a108); right = vget_high_u8(a109); both = vzip_u8(left, right); - ((uint8x16_t*)Y)[3] = vcombine_u8(both.val[0], both.val[1]); + NEONSPIRAL_STORE(Y + 3 * 16, vcombine_u8(both.val[0], both.val[1])); // Renormalize uint8x16_t m5, m6; - m5 = ((uint8x16_t*)Y)[0]; - m5 = vminq_u8(m5, ((uint8x16_t*)Y)[1]); - m5 = vminq_u8(m5, ((uint8x16_t*)Y)[2]); - m5 = vminq_u8(m5, ((uint8x16_t*)Y)[3]); + m5 = NEONSPIRAL_LOAD(Y); + m5 = vminq_u8(m5, NEONSPIRAL_LOAD(Y + 1 * 16)); + m5 = vminq_u8(m5, NEONSPIRAL_LOAD(Y + 2 * 16)); + m5 = vminq_u8(m5, NEONSPIRAL_LOAD(Y + 3 * 16)); uint8x8_t m7; m7 = vpmin_u8(vget_low_u8(m5), vget_high_u8(m5)); m7 = vpmin_u8(m7, m7); m7 = vpmin_u8(m7, m7); m7 = vpmin_u8(m7, m7); m6 = vcombine_u8(m7, m7); - ((uint8x16_t*)Y)[0] = vqsubq_u8(((uint8x16_t*)Y)[0], m6); - ((uint8x16_t*)Y)[1] = vqsubq_u8(((uint8x16_t*)Y)[1], m6); - ((uint8x16_t*)Y)[2] = vqsubq_u8(((uint8x16_t*)Y)[2], m6); - ((uint8x16_t*)Y)[3] = vqsubq_u8(((uint8x16_t*)Y)[3], m6); + NEONSPIRAL_STORE(Y, vqsubq_u8(NEONSPIRAL_LOAD(Y), m6)); + NEONSPIRAL_STORE(Y + 1 * 16, vqsubq_u8(NEONSPIRAL_LOAD(Y + 1 * 16), m6)); + NEONSPIRAL_STORE(Y + 2 * 16, vqsubq_u8(NEONSPIRAL_LOAD(Y + 2 * 16), m6)); + NEONSPIRAL_STORE(Y + 3 * 16, vqsubq_u8(NEONSPIRAL_LOAD(Y + 3 * 16), m6)); // Swap pointers to old and new metrics tmp = X; @@ -436,6 +447,9 @@ static inline void volk_8u_x4_conv_k7_r2_8u_neonspiral(unsigned char* Y, } } +#undef NEONSPIRAL_LOAD +#undef NEONSPIRAL_STORE + #endif /*LV_HAVE_NEON*/ #if LV_HAVE_GENERIC diff --git a/lib/kernel_tests.h b/lib/kernel_tests.h index 4711ced8b..a0ee446ca 100644 --- a/lib/kernel_tests.h +++ b/lib/kernel_tests.h @@ -87,9 +87,12 @@ std::vector init_test_list(volk_test_params_t test_params) QA(VOLK_INIT_TEST(volk_32f_accumulator_s32f, test_params.make_absolute(2e-2))) QA(VOLK_INIT_TEST(volk_32f_x2_add_32f, test_params)) - // Index kernels need identical values to test tie-breaking (first index wins) + // Index kernels need identical values to test tie-breaking (first index wins). + // The leading 0.0f puts the first maximum at index 1 rather than 0, so a + // kernel that tracks indices in the wrong lanes reports the wrong one. volk_test_params_t test_params_index(test_params.make_tol(0)); test_params_index.add_float_edge_cases({ + 0.0f, 1.0f, 1.0f, 1.0f,