From 1d9d8b6e0fcd2cd95b3c5372c3755e1e4dcfb700 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Thu, 10 Sep 2026 21:55:13 +0200 Subject: [PATCH 01/13] Add missing Vector2 overload --- include/weaver/memory.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/weaver/memory.h b/include/weaver/memory.h index e7043f7..fe038b9 100644 --- a/include/weaver/memory.h +++ b/include/weaver/memory.h @@ -122,6 +122,15 @@ namespace weaver { value = VectorScalar(base, 1); } + template + WEAVER_STRONG_INLINE void load( + Eigen::Matrix, 2, 1>& loaded, + const Eigen::Matrix* base) + { + loaded(0) = VectorScalar(base->data() + 0, 2); + loaded(1) = VectorScalar(base->data() + 1, 2); + } + template WEAVER_STRONG_INLINE void load( Eigen::Matrix, 3, 1>& loaded, From 1ff96552cfe0be2d245387657acd818134c6fdb5 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Thu, 10 Sep 2026 21:55:35 +0200 Subject: [PATCH 02/13] Allow to build from local submodules in addition to Conan2 dependencies --- CMakeLists.txt | 34 +++++++++++++++++++++++-- cmake/FindEigen3.cmake | 57 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 cmake/FindEigen3.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 48fbfc8..ff1e2f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,23 @@ cmake_policy(SET CMP0048 NEW) project(FloatWeaver VERSION 0.1.0.0 LANGUAGES CXX) -find_package(Eigen3 REQUIRED) +# Eigen3: use a Conan/system package if one is available; otherwise fall back +# to the "external/eigen" git submodule, so the project also builds with +# nothing more than `git submodule update --init`. +find_package(Eigen3 3.4 QUIET) +if(NOT TARGET Eigen3::Eigen) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/eigen/CMakeLists.txt") + message(STATUS "Eigen3 package not found, building from external/eigen submodule") + set(EIGEN3_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/eigen") + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + find_package(Eigen3 3.4 MODULE REQUIRED) + else() + message(FATAL_ERROR + "Eigen3 was not found, and external/eigen is not checked out. Either " + "install Eigen3 (e.g. 'conan install .') or run " + "'git submodule update --init external/eigen'.") + endif() +endif() include(cmake/config.cmake) @@ -88,7 +104,21 @@ weaver_target_sources(weaver "." ${SRC}) # Only build tests when building as standalone project if("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") - find_package(Catch2 REQUIRED) + # Catch2: use a Conan/system package if one is available; otherwise fall + # back to the "external/catch2" git submodule. + find_package(Catch2 3 QUIET) + if(NOT TARGET Catch2::Catch2WithMain) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/catch2/CMakeLists.txt") + message(STATUS "Catch2 package not found, building from external/catch2 submodule") + add_subdirectory(external/catch2 EXCLUDE_FROM_ALL) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/catch2/extras") + else() + message(FATAL_ERROR + "Catch2 was not found, and external/catch2 is not checked out. Either " + "install Catch2 (e.g. 'conan install .') or run " + "'git submodule update --init external/catch2'.") + endif() + endif() add_executable(weaver_tests "") set(WEAVER_TEST_SRC diff --git a/cmake/FindEigen3.cmake b/cmake/FindEigen3.cmake new file mode 100644 index 0000000..987caf9 --- /dev/null +++ b/cmake/FindEigen3.cmake @@ -0,0 +1,57 @@ +# - Try to find Eigen3, without relying on an Eigen3Config.cmake being installed. +# +# Used as a fallback when Eigen3 isn't available as a package (Conan, system +# install): point EIGEN3_INCLUDE_DIR at a source checkout (e.g. the +# external/eigen git submodule) before calling find_package(Eigen3 MODULE), +# and this module will pick up the version from the headers directly. +# +# Once done this will define +# +# EIGEN3_FOUND - Eigen3 was found +# EIGEN3_INCLUDE_DIR - the Eigen3 include directory +# EIGEN3_VERSION - Eigen3 version +# Eigen3::Eigen - imported target, usable like the Conan/CMake package +# +# Reads hints about search locations from the EIGEN3_ROOT / EIGEN3_ROOT_DIR +# environment variables when EIGEN3_INCLUDE_DIR isn't already set. + +if(NOT EIGEN3_INCLUDE_DIR) + find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + HINTS + ENV EIGEN3_ROOT + ENV EIGEN3_ROOT_DIR + PATHS + ${CMAKE_INSTALL_PREFIX}/include + PATH_SUFFIXES eigen3 eigen + ) +endif() + +if(EIGEN3_INCLUDE_DIR AND EXISTS "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h") + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _ "${_eigen3_version_header}") + set(_eigen3_world_version "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _ "${_eigen3_version_header}") + set(_eigen3_major_version "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _ "${_eigen3_version_header}") + set(_eigen3_minor_version "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION "${_eigen3_world_version}.${_eigen3_major_version}.${_eigen3_minor_version}") + unset(_eigen3_version_header) + unset(_eigen3_world_version) + unset(_eigen3_major_version) + unset(_eigen3_minor_version) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Eigen3 + REQUIRED_VARS EIGEN3_INCLUDE_DIR + VERSION_VAR EIGEN3_VERSION +) + +if(EIGEN3_FOUND AND NOT TARGET Eigen3::Eigen) + add_library(Eigen3::Eigen INTERFACE IMPORTED) + target_include_directories(Eigen3::Eigen INTERFACE "${EIGEN3_INCLUDE_DIR}") +endif() + +mark_as_advanced(EIGEN3_INCLUDE_DIR) From 891fdca42835ba92668e20f0adbe62365931541c Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 21:11:06 +0200 Subject: [PATCH 03/13] Share the test numbers --- CMakeLists.txt | 3 +++ src/tests/bool.cpp | 13 +------------ src/tests/common.h | 37 +++---------------------------------- src/tests/common_bool.h | 20 ++++++++++++++++++++ src/tests/common_float.h | 18 ++++++++++++++++++ src/tests/common_int.h | 18 ++++++++++++++++++ src/tests/float.cpp | 11 +---------- src/tests/int.cpp | 11 +---------- 8 files changed, 65 insertions(+), 66 deletions(-) create mode 100644 src/tests/common_bool.h create mode 100644 src/tests/common_float.h create mode 100644 src/tests/common_int.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ff1e2f1..bb034c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,9 @@ if("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") src/tests/int.cpp src/tests/common.h + src/tests/common_bool.h + src/tests/common_float.h + src/tests/common_int.h src/tests/conditional.cpp src/tests/eigen_alignedbox.cpp diff --git a/src/tests/bool.cpp b/src/tests/bool.cpp index bd35d26..3c63a1a 100644 --- a/src/tests/bool.cpp +++ b/src/tests/bool.cpp @@ -39,18 +39,7 @@ namespace weaver using bool16 = VectorScalar; } -#define WEAVER_SIMD_BOOLS \ - using bool4 = weaver::bool4; \ - using bool8 = weaver::bool8; \ - using bool16 = weaver::bool16; \ - bool4 b4_0{ true, false, true, false }; \ - bool4 b4_1{ false, true, false, true }; \ - bool8 b8_0{ true, false, true, false, true, false, true, false }; \ - bool8 b8_1{ false, true, false, true, false, true, false, true }; \ - bool16 b16_0{ true, false, true, false, true, false, true, false, \ - true, false, true, false, true, false, true, false }; \ - bool16 b16_1{ false, true, false, true, false, true, false, true, \ - false, true, false, true, false, true, false, true } +#include "common_bool.h" TEST_CASE("BoolN", "[bool][operation]") { diff --git a/src/tests/common.h b/src/tests/common.h index 815bdbf..5578238 100644 --- a/src/tests/common.h +++ b/src/tests/common.h @@ -14,37 +14,6 @@ // FloatWeaver #include -#define WEAVER_SIMD_BOOLS \ - using bool4 = weaver::bool4; \ - using bool8 = weaver::bool8; \ - using bool16 = weaver::bool16; \ - bool4 b4_0{ true, false, true, false }; \ - bool4 b4_1{ false, true, false, true }; \ - bool8 b8_0{ true, false, true, false, true, false, true, false }; \ - bool8 b8_1{ false, true, false, true, false, true, false, true }; \ - bool16 b16_0{ true, false, true, false, true, false, true, false, \ - true, false, true, false, true, false, true, false }; \ - bool16 b16_1{ false, true, false, true, false, true, false, true, \ - false, true, false, true, false, true, false, true } - -#define WEAVER_SIMD_FLOATS \ - using float4 = weaver::float4; \ - using float8 = weaver::float8; \ - using float16 = weaver::float16; \ - float4 f4_asc{ 1, 2, 3, 4 }; \ - float4 f4_desc{ 4, 3, 2, 1 }; \ - float8 f8_asc{ 1, 2, 3, 4, 5, 6, 7, 8 }; \ - float8 f8_desc{ 8, 7, 6, 5, 4, 3, 2, 1 }; \ - float16 f16_asc{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; \ - float16 f16_desc{ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } - -#define WEAVER_SIMD_INTS \ - using int4 = weaver::int4; \ - using int8 = weaver::int8; \ - using int16 = weaver::int16; \ - int4 i4_asc{ 1, 2, 3, 4 }; \ - int4 i4_desc{ 4, 3, 2, 1 }; \ - int8 i8_asc{ 1, 2, 3, 4, 5, 6, 7, 8 }; \ - int8 i8_desc{ 8, 7, 6, 5, 4, 3, 2, 1 }; \ - int16 i16_asc{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; \ - int16 i16_desc{ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } +#include "common_bool.h" +#include "common_float.h" +#include "common_int.h" diff --git a/src/tests/common_bool.h b/src/tests/common_bool.h new file mode 100644 index 0000000..b837231 --- /dev/null +++ b/src/tests/common_bool.h @@ -0,0 +1,20 @@ +// Copyright 2023 Basil Fierz +// +// Use of this source code is governed by an MIT - style +// license that can be found in the LICENSE file or at +// https ://opensource.org/licenses/MIT. + +#pragma once + +#define WEAVER_SIMD_BOOLS \ + using bool4 = weaver::bool4; \ + using bool8 = weaver::bool8; \ + using bool16 = weaver::bool16; \ + bool4 b4_0{ true, false, true, false }; \ + bool4 b4_1{ false, true, false, true }; \ + bool8 b8_0{ true, false, true, false, true, false, true, false }; \ + bool8 b8_1{ false, true, false, true, false, true, false, true }; \ + bool16 b16_0{ true, false, true, false, true, false, true, false, \ + true, false, true, false, true, false, true, false }; \ + bool16 b16_1{ false, true, false, true, false, true, false, true, \ + false, true, false, true, false, true, false, true } diff --git a/src/tests/common_float.h b/src/tests/common_float.h new file mode 100644 index 0000000..ce96ba7 --- /dev/null +++ b/src/tests/common_float.h @@ -0,0 +1,18 @@ +// Copyright 2023 Basil Fierz +// +// Use of this source code is governed by an MIT - style +// license that can be found in the LICENSE file or at +// https ://opensource.org/licenses/MIT. + +#pragma once + +#define WEAVER_SIMD_FLOATS \ + using float4 = weaver::float4; \ + using float8 = weaver::float8; \ + using float16 = weaver::float16; \ + float4 f4_asc{ 1, 2, 3, 4 }; \ + float4 f4_desc{ 4, 3, 2, 1 }; \ + float8 f8_asc{ 1, 2, 3, 4, 5, 6, 7, 8 }; \ + float8 f8_desc{ 8, 7, 6, 5, 4, 3, 2, 1 }; \ + float16 f16_asc{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; \ + float16 f16_desc{ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } diff --git a/src/tests/common_int.h b/src/tests/common_int.h new file mode 100644 index 0000000..9c7263f --- /dev/null +++ b/src/tests/common_int.h @@ -0,0 +1,18 @@ +// Copyright 2023 Basil Fierz +// +// Use of this source code is governed by an MIT - style +// license that can be found in the LICENSE file or at +// https ://opensource.org/licenses/MIT. + +#pragma once + +#define WEAVER_SIMD_INTS \ + using int4 = weaver::int4; \ + using int8 = weaver::int8; \ + using int16 = weaver::int16; \ + int4 i4_asc{ 1, 2, 3, 4 }; \ + int4 i4_desc{ 4, 3, 2, 1 }; \ + int8 i8_asc{ 1, 2, 3, 4, 5, 6, 7, 8 }; \ + int8 i8_desc{ 8, 7, 6, 5, 4, 3, 2, 1 }; \ + int16 i16_asc{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; \ + int16 i16_desc{ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } diff --git a/src/tests/float.cpp b/src/tests/float.cpp index 08e634e..421582f 100644 --- a/src/tests/float.cpp +++ b/src/tests/float.cpp @@ -39,16 +39,7 @@ namespace weaver using float16 = VectorScalar; } -#define WEAVER_SIMD_FLOATS \ - using float4 = weaver::float4; \ - using float8 = weaver::float8; \ - using float16 = weaver::float16; \ - float4 f4_asc{ 1, 2, 3, 4 }; \ - float4 f4_desc{ 4, 3, 2, 1 }; \ - float8 f8_asc{ 1, 2, 3, 4, 5, 6, 7, 8 }; \ - float8 f8_desc{ 8, 7, 6, 5, 4, 3, 2, 1 }; \ - float16 f16_asc{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; \ - float16 f16_desc{ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } +#include "common_float.h" TEST_CASE("FloatN", "[float][operation]") { diff --git a/src/tests/int.cpp b/src/tests/int.cpp index 72d068d..da40c99 100644 --- a/src/tests/int.cpp +++ b/src/tests/int.cpp @@ -39,16 +39,7 @@ namespace weaver using int16 = VectorScalar; } -#define WEAVER_SIMD_INTS \ - using int4 = weaver::int4; \ - using int8 = weaver::int8; \ - using int16 = weaver::int16; \ - int4 i4_asc{ 1, 2, 3, 4 }; \ - int4 i4_desc{ 4, 3, 2, 1 }; \ - int8 i8_asc{ 1, 2, 3, 4, 5, 6, 7, 8 }; \ - int8 i8_desc{ 8, 7, 6, 5, 4, 3, 2, 1 }; \ - int16 i16_asc{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; \ - int16 i16_desc{ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 } +#include "common_int.h" TEST_CASE("IntN", "[int][operation]") { From 2ebdd5af192eccf3e83684294e031aebcfb30f62 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 21:36:10 +0200 Subject: [PATCH 04/13] Extend build tests to other x64 vectorizations --- .github/workflows/build.yml | 49 ++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 963226b..173e700 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,12 @@ jobs: os: [ubuntu-latest, windows-latest] build_type: [Release] c_compiler: [gcc-12, clang-15, cl, emcc] + # One leg per WEAVER_VECTORIZE backend, so every op-codegen change is + # actually compiled (and, where the runner's CPU allows it, run) + # against all of ref/SSE/AVX/AVX2/AVX512 rather than just the SSE 4.2 + # default. NEON is not covered here: none of the hosted runners below + # are ARM. + vectorize: ['Generic', 'SSE 4.2', 'AVX', 'AVX 2', 'AVX 512'] include: - os: windows-latest c_compiler: cl @@ -35,6 +41,14 @@ jobs: c_compiler: emcc - os: ubuntu-latest c_compiler: cl + # Emscripten's headers only emulate up to SSE 4.2 over wasm32 SIMD128; + # there is no __m256/__m512 support to target AVX/AVX2/AVX512 with. + - c_compiler: emcc + vectorize: 'AVX' + - c_compiler: emcc + vectorize: 'AVX 2' + - c_compiler: emcc + vectorize: 'AVX 512' steps: - uses: actions/checkout@v3 @@ -99,17 +113,46 @@ jobs: -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_TOOLCHAIN_FILE=${{ steps.strings.outputs.generators-dir }}/conan_toolchain.cmake + -DWEAVER_VECTORIZE="${{ matrix.vectorize }}" - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - - name: Test - if: "${{ matrix.c_compiler != 'emcc' }}" + - name: Test (Linux) + if: "${{ runner.os == 'Linux' && matrix.c_compiler != 'emcc' }}" working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} + # + # AVX-512 is special-cased: GitHub-hosted runners don't guarantee AVX-512 + # CPU support even though the build above always compiles it (that's what + # catches cross-backend interface bugs). If this runner's CPU lacks it, + # skip running rather than fail the job on an unrelated SIGILL. + run: | + if [ "${{ matrix.vectorize }}" = "AVX 512" ]; then + if ! grep -qE '\bavx512f\b' /proc/cpuinfo || ! grep -qE '\bavx512vl\b' /proc/cpuinfo || ! grep -qE '\bavx512dq\b' /proc/cpuinfo; then + echo "::warning::Skipping AVX-512 tests: this runner's CPU does not support avx512f/avx512vl/avx512dq." + exit 0 + fi + fi + ctest --build-config ${{ matrix.build_type }} + + - name: Test (Windows) + if: "${{ runner.os == 'Windows' }}" + working-directory: ${{ steps.strings.outputs.build-output-dir }} + shell: pwsh + # Same AVX-512 caveat as the Linux step above, checked via the .NET + # intrinsics support flag instead of /proc/cpuinfo. + run: | + if ("${{ matrix.vectorize }}" -eq "AVX 512") { + if (-not [System.Runtime.Intrinsics.X86.Avx512F]::IsSupported) { + Write-Host "::warning::Skipping AVX-512 tests: this runner's CPU does not support AVX-512." + exit 0 + } + } + ctest --build-config ${{ matrix.build_type }} + exit $LASTEXITCODE - name: Test (Emscripten) if: "${{ matrix.c_compiler == 'emcc' }}" From b7c43a0e9a7dd1268d0415b3c1991bdd6e8f0f12 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 21:56:46 +0200 Subject: [PATCH 05/13] Extend build tests to ARM --- .github/workflows/build.yml | 80 +++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 8 +++- cmake-conan/aarch64_profile | 11 +++++ cmake-conan/arm_profile | 11 +++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 cmake-conan/aarch64_profile create mode 100644 cmake-conan/arm_profile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 173e700..4844015 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,3 +160,83 @@ jobs: # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: node weaver_tests.js + + # No GitHub-hosted runner is ARM, so the NEON backend is cross-compiled here + # on an x86 runner and its tests are run through QEMU user-mode emulation + # instead. CMAKE_CROSSCOMPILING_EMULATOR (set explicitly below, since Conan + # doesn't know about QEMU) makes both `cmake --build` and `ctest` transparently + # exec test binaries as e.g. `qemu-arm -L /usr/arm-linux-gnueabihf `. + build-arm: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + include: + - arch: arm + triple: arm-linux-gnueabihf + qemu: qemu-arm + profile: cmake-conan/arm_profile + - arch: aarch64 + triple: aarch64-linux-gnu + qemu: qemu-aarch64 + profile: cmake-conan/aarch64_profile + + steps: + - uses: actions/checkout@v3 + + - name: Install cross-compilation toolchain and QEMU + run: | + sudo apt-get update + sudo apt-get install -y qemu-user "gcc-12-${{ matrix.triple }}" "g++-12-${{ matrix.triple }}" + + - name: Set reusable strings + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + echo "generators-dir=${{ github.workspace }}/build/build/Release/generators" >> "$GITHUB_OUTPUT" + + - name: Cache pip packages + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }}-${{ hashFiles('**/requirements.txt') }} + restore-keys: pip-${{ runner.os }}- + + - name: Install Conan2 + run: pip install conan + + - name: Cache Conan dependencies + id: conan-cache + uses: actions/cache@v3 + with: + path: ~/.conan2 + key: conan2-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('**/conanfile.*') }} + restore-keys: conan2-${{ runner.os }}-${{ matrix.arch }}- + + - name: Detect default Conan profile + if: steps.conan-cache.outputs.cache-hit != 'true' + run: conan profile detect + + - name: Install Conan Dependencies + run: > + conan install --build=missing -pr:h ${{ matrix.profile }} -of ${{ steps.strings.outputs.build-output-dir }} ${{ github.workspace }} + + - name: Configure CMake + run: > + cmake -S ${{ github.workspace }} + -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_TOOLCHAIN_FILE=${{ steps.strings.outputs.generators-dir }}/conan_toolchain.cmake + -DWEAVER_VECTORIZE=NEON + -DCMAKE_CROSSCOMPILING_EMULATOR="${{ matrix.qemu }};-L;/usr/${{ matrix.triple }}" + + - name: Build + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release + + - name: Test (QEMU) + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Runs transparently under QEMU via CMAKE_CROSSCOMPILING_EMULATOR above. + run: ctest --build-config Release --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index bb034c2..802320c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,13 @@ if("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") ) target_link_libraries(weaver_tests weaver Catch2::Catch2WithMain) - if(NOT CMAKE_CROSSCOMPILING) + # catch_discover_tests() needs to run the freshly-built test executable to + # enumerate its test cases. When cross-compiling that only works if CMake + # knows how to execute foreign-architecture binaries, i.e. when + # CMAKE_CROSSCOMPILING_EMULATOR is set (e.g. "qemu-arm -L ..." for a NEON + # build cross-compiled for ARM/AArch64). Plain Emscripten builds don't set + # it -- CI drives those through `node weaver_tests.js` directly instead. + if(NOT CMAKE_CROSSCOMPILING OR (CMAKE_CROSSCOMPILING_EMULATOR AND NOT EMSCRIPTEN)) include(CTest) include(Catch) catch_discover_tests(weaver_tests) diff --git a/cmake-conan/aarch64_profile b/cmake-conan/aarch64_profile new file mode 100644 index 0000000..cb6538b --- /dev/null +++ b/cmake-conan/aarch64_profile @@ -0,0 +1,11 @@ +[settings] +os=Linux +arch=armv8 +compiler=gcc +compiler.version=12 +compiler.libcxx=libstdc++11 +compiler.cppstd=17 +build_type=Release + +[conf] +tools.build:compiler_executables={"c": "aarch64-linux-gnu-gcc-12", "cpp": "aarch64-linux-gnu-g++-12"} diff --git a/cmake-conan/arm_profile b/cmake-conan/arm_profile new file mode 100644 index 0000000..96199ad --- /dev/null +++ b/cmake-conan/arm_profile @@ -0,0 +1,11 @@ +[settings] +os=Linux +arch=armv7hf +compiler=gcc +compiler.version=12 +compiler.libcxx=libstdc++11 +compiler.cppstd=17 +build_type=Release + +[conf] +tools.build:compiler_executables={"c": "arm-linux-gnueabihf-gcc-12", "cpp": "arm-linux-gnueabihf-g++-12"} From fa860dbf6741ab5afa880657de36b2fc1229355c Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 22:38:24 +0200 Subject: [PATCH 06/13] Update to supported clang version --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4844015..d290a82 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] build_type: [Release] - c_compiler: [gcc-12, clang-15, cl, emcc] + c_compiler: [gcc-12, clang-18, cl, emcc] # One leg per WEAVER_VECTORIZE backend, so every op-codegen change is # actually compiled (and, where the runner's CPU allows it, run) # against all of ref/SSE/AVX/AVX2/AVX512 rather than just the SSE 4.2 @@ -30,13 +30,13 @@ jobs: c_compiler: gcc-12 cxx_compiler: g++-12 - os: ubuntu-latest - c_compiler: clang-15 - cxx_compiler: clang++-15 + c_compiler: clang-18 + cxx_compiler: clang++-18 exclude: - os: windows-latest c_compiler: gcc-12 - os: windows-latest - c_compiler: clang-15 + c_compiler: clang-18 - os: windows-latest c_compiler: emcc - os: ubuntu-latest @@ -54,7 +54,7 @@ jobs: - uses: actions/checkout@v3 - name: Setup build toolchain configuration - if: "${{ matrix.c_compiler == 'gcc-12' || matrix.c_compiler == 'clang-15' }}" + if: "${{ matrix.c_compiler == 'gcc-12' || matrix.c_compiler == 'clang-18' }}" run: | echo "CC=${{ matrix.c_compiler }}" >> $GITHUB_ENV echo "CXX=${{ matrix.cxx_compiler }}" >> $GITHUB_ENV From 4e7cb95f580e1aaa0c3581ed08a3c577db3a3f7e Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 22:59:08 +0200 Subject: [PATCH 07/13] Add missing includes to ref-implementation --- src/tests/bool.cpp | 1 + src/tests/float.cpp | 1 + src/tests/int.cpp | 1 + 3 files changed, 3 insertions(+) diff --git a/src/tests/bool.cpp b/src/tests/bool.cpp index 3c63a1a..2b09aca 100644 --- a/src/tests/bool.cpp +++ b/src/tests/bool.cpp @@ -30,6 +30,7 @@ # include # include # include +# include #endif namespace weaver diff --git a/src/tests/float.cpp b/src/tests/float.cpp index 421582f..8c9ddc6 100644 --- a/src/tests/float.cpp +++ b/src/tests/float.cpp @@ -30,6 +30,7 @@ # include # include # include +# include #endif namespace weaver diff --git a/src/tests/int.cpp b/src/tests/int.cpp index da40c99..43d358f 100644 --- a/src/tests/int.cpp +++ b/src/tests/int.cpp @@ -30,6 +30,7 @@ # include # include # include +# include #endif namespace weaver From 10f6ad4d053c5eb9d64b913e492d19938b6d3bad Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 22:59:48 +0200 Subject: [PATCH 08/13] Include all intrinsics --- src/intrinsics_avx512.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/intrinsics_avx512.cpp b/src/intrinsics_avx512.cpp index 64b4078..fcfedda 100644 --- a/src/intrinsics_avx512.cpp +++ b/src/intrinsics_avx512.cpp @@ -15,8 +15,7 @@ WEAVER_BEGIN_EXTERNAL_HEADERS WEAVER_END_EXTERNAL_HEADERS // FloatWeaver -# include -# include +# include namespace weaver { # if !defined(WEAVER_COMPILER_MSVC) From f40afcbc7dcbbc2d827b14b88b4a2ea202da1948 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 23:00:19 +0200 Subject: [PATCH 09/13] Add avx2 flags for avx512 --- cmake/config.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/config.cmake b/cmake/config.cmake index fecc087..ecd2a34 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -103,7 +103,10 @@ function(weaver_configure_target tgt) # Make SIMD available if(WEAVER_VECTORIZE_AVX512) - target_compile_options(${tgt} PUBLIC "-mavx512f" "-mavx512vl" "-mavx512dq") + # Every real AVX512F CPU also has AVX2/FMA3; Eigen's + # ConfigureVectorization.h refuses to compile AVX-512 code without + # -mfma since GCC/Clang can otherwise miscompile it. + target_compile_options(${tgt} PUBLIC "-mavx512f" "-mavx512vl" "-mavx512dq" "-mavx2" "-mfma") elseif(WEAVER_VECTORIZE_AVX2) target_compile_options(${tgt} PUBLIC "-mavx2" "-mfma") elseif(WEAVER_VECTORIZE_AVX) From ef6856d883d3efc073fb64a8ff4aecb0f334d1b3 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 23:02:03 +0200 Subject: [PATCH 10/13] Only include sse header if requested --- include/weaver/config.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/weaver/config.h b/include/weaver/config.h index cfa60fa..9a74be2 100644 --- a/include/weaver/config.h +++ b/include/weaver/config.h @@ -179,7 +179,9 @@ extern "C" # define WEAVER_VECTORIZE_SSE # endif # endif -# include +# ifdef WEAVER_VECTORIZE_SSE +# include +# endif #endif #if defined WEAVER_COMPILER_MSVC From 684c5d0bd0a6c9f5665d4614d59dd213daee3ded Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 23:04:20 +0200 Subject: [PATCH 11/13] Missing store implementations --- include/weaver/memory.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/weaver/memory.h b/include/weaver/memory.h index fe038b9..8c0f8b2 100644 --- a/include/weaver/memory.h +++ b/include/weaver/memory.h @@ -152,6 +152,38 @@ namespace weaver { loaded(3) = VectorScalar(base->data() + 3, 4); } + template + WEAVER_STRONG_INLINE void store(T* base, const VectorScalar& value) + { + for (int i = 0; i < Width; i++) + base[i] = value[i]; + } + + template + WEAVER_STRONG_INLINE void store( + Eigen::Matrix* base, + const Eigen::Matrix, 2, 1>& value) + { + for (int i = 0; i < Width; i++) + { + base[i](0) = value(0)[i]; + base[i](1) = value(1)[i]; + } + } + + template + WEAVER_STRONG_INLINE void store( + Eigen::Matrix* base, + const Eigen::Matrix, 3, 1>& value) + { + for (int i = 0; i < Width; i++) + { + base[i](0) = value(0)[i]; + base[i](1) = value(1)[i]; + base[i](2) = value(2)[i]; + } + } + template VectorScalar gather(T const* base, VectorScalar vindex) { From 0474584116f274270998c215c4e0bd1c00dc4e8f Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 23:09:50 +0200 Subject: [PATCH 12/13] Missing instructions on armv7hf --- include/weaver/float16_neon.h | 6 +++--- include/weaver/float4_neon.h | 6 +++--- include/weaver/float8_neon.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/weaver/float16_neon.h b/include/weaver/float16_neon.h index 51aa38e..265dafb 100644 --- a/include/weaver/float16_neon.h +++ b/include/weaver/float16_neon.h @@ -20,12 +20,12 @@ namespace weaver { WEAVER_SIMD_BINARY_OP(operator+, vaddq_f32, 4) WEAVER_SIMD_BINARY_OP(operator-, vsubq_f32, 4) WEAVER_SIMD_BINARY_OP(operator*, vmulq_f32, 4) - WEAVER_SIMD_BINARY_OP(operator/, vdivq_f32, 4) + WEAVER_SIMD_BINARY_OP(operator/, weaver_vdivq_f32, 4) WEAVER_SIMD_ASSIGN_OP(operator+=, vaddq_f32, 4) WEAVER_SIMD_ASSIGN_OP(operator-=, vsubq_f32, 4) WEAVER_SIMD_ASSIGN_OP(operator*=, vmulq_f32, 4) - WEAVER_SIMD_ASSIGN_OP(operator/=, vdivq_f32, 4) + WEAVER_SIMD_ASSIGN_OP(operator/=, weaver_vdivq_f32, 4) WEAVER_SIMD_COMP_OP(operator==, vceqq_f32, 4) WEAVER_SIMD_COMP_OP(operator!=, vcneqq_f32, 4) @@ -40,7 +40,7 @@ namespace weaver { WEAVER_SIMD_UNARY_OP(exp, vexpq_f32, 4) WEAVER_SIMD_UNARY_OP(log, vlogq_f32, 4) WEAVER_SIMD_UNARY_OP(sgn, vsgnq_f32, 4) - WEAVER_SIMD_UNARY_OP(sqrt, vsqrtq_f32, 4) + WEAVER_SIMD_UNARY_OP(sqrt, weaver_vsqrtq_f32, 4) WEAVER_SIMD_UNARY_OP(rcp, vrcpq_f32, 4) WEAVER_SIMD_UNARY_OP(rsqrt, vrsqrtq_f32, 4) diff --git a/include/weaver/float4_neon.h b/include/weaver/float4_neon.h index b1ef395..8e19ece 100644 --- a/include/weaver/float4_neon.h +++ b/include/weaver/float4_neon.h @@ -20,12 +20,12 @@ namespace weaver { WEAVER_SIMD_BINARY_OP(operator+, vaddq_f32, 1) WEAVER_SIMD_BINARY_OP(operator-, vsubq_f32, 1) WEAVER_SIMD_BINARY_OP(operator*, vmulq_f32, 1) - WEAVER_SIMD_BINARY_OP(operator/, vdivq_f32, 1) + WEAVER_SIMD_BINARY_OP(operator/, weaver_vdivq_f32, 1) WEAVER_SIMD_ASSIGN_OP(operator+=, vaddq_f32, 1) WEAVER_SIMD_ASSIGN_OP(operator-=, vsubq_f32, 1) WEAVER_SIMD_ASSIGN_OP(operator*=, vmulq_f32, 1) - WEAVER_SIMD_ASSIGN_OP(operator/=, vdivq_f32, 1) + WEAVER_SIMD_ASSIGN_OP(operator/=, weaver_vdivq_f32, 1) WEAVER_SIMD_COMP_OP(operator==, vceqq_f32, 1) WEAVER_SIMD_COMP_OP(operator!=, vcneqq_f32, 1) @@ -40,7 +40,7 @@ namespace weaver { WEAVER_SIMD_UNARY_OP(exp, vexpq_f32, 1) WEAVER_SIMD_UNARY_OP(log, vlogq_f32, 1) WEAVER_SIMD_UNARY_OP(sgn, vsgnq_f32, 1) - WEAVER_SIMD_UNARY_OP(sqrt, vsqrtq_f32, 1) + WEAVER_SIMD_UNARY_OP(sqrt, weaver_vsqrtq_f32, 1) WEAVER_SIMD_UNARY_OP(rcp, vrcpq_f32, 1) WEAVER_SIMD_UNARY_OP(rsqrt, vrsqrtq_f32, 1) diff --git a/include/weaver/float8_neon.h b/include/weaver/float8_neon.h index 5bf9135..6607650 100644 --- a/include/weaver/float8_neon.h +++ b/include/weaver/float8_neon.h @@ -20,12 +20,12 @@ namespace weaver { WEAVER_SIMD_BINARY_OP(operator+, vaddq_f32, 2) WEAVER_SIMD_BINARY_OP(operator-, vsubq_f32, 2) WEAVER_SIMD_BINARY_OP(operator*, vmulq_f32, 2) - WEAVER_SIMD_BINARY_OP(operator/, vdivq_f32, 2) + WEAVER_SIMD_BINARY_OP(operator/, weaver_vdivq_f32, 2) WEAVER_SIMD_ASSIGN_OP(operator+=, vaddq_f32, 2) WEAVER_SIMD_ASSIGN_OP(operator-=, vsubq_f32, 2) WEAVER_SIMD_ASSIGN_OP(operator*=, vmulq_f32, 2) - WEAVER_SIMD_ASSIGN_OP(operator/=, vdivq_f32, 2) + WEAVER_SIMD_ASSIGN_OP(operator/=, weaver_vdivq_f32, 2) WEAVER_SIMD_COMP_OP(operator==, vceqq_f32, 2) WEAVER_SIMD_COMP_OP(operator!=, vcneqq_f32, 2) @@ -40,7 +40,7 @@ namespace weaver { WEAVER_SIMD_UNARY_OP(exp, vexpq_f32, 2) WEAVER_SIMD_UNARY_OP(log, vlogq_f32, 2) WEAVER_SIMD_UNARY_OP(sgn, vsgnq_f32, 2) - WEAVER_SIMD_UNARY_OP(sqrt, vsqrtq_f32, 2) + WEAVER_SIMD_UNARY_OP(sqrt, weaver_vsqrtq_f32, 2) WEAVER_SIMD_UNARY_OP(rcp, vrcpq_f32, 2) WEAVER_SIMD_UNARY_OP(rsqrt, vrsqrtq_f32, 2) From 1b112ca718baab3af1c8542a61f64c6e86350547 Mon Sep 17 00:00:00 2001 From: Basil Fierz Date: Fri, 11 Sep 2026 23:32:50 +0200 Subject: [PATCH 13/13] Adds missing header for index_sequence --- include/weaver/common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/weaver/common.h b/include/weaver/common.h index 56e1059..b93a869 100644 --- a/include/weaver/common.h +++ b/include/weaver/common.h @@ -10,6 +10,7 @@ #include #include #include +#include // FloatWeaver #include