From 5357ffc461a92006f127d586597272a0c00d787d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Lorenzo=20Rodr=C3=ADguez?= Date: Tue, 30 Jun 2026 14:44:06 +0200 Subject: [PATCH] Support aarch64 / arm architectures (e.g. AWS Graviton) The package previously forced a build failure on non-x86 architectures via an unsatisfiable `base < 0` constraint, because the C sources and compiler flags assumed x86 SIMD intrinsics. This makes the library build and run on aarch64 / arm, falling back to the pure-Haskell "Stock" implementations: - cabal: only emit the x86 instruction-set flags (-mavx2, -mbmi2, -msse4.2) when building for an x86 target (arch(x86_64) || arch(i386)); remove the `base < 0` block. - cbits/simd_avx2.c: guard the x86 intrinsic includes behind AVX2_ENABLED (the function bodies were already so guarded, so they become no-ops). - cbits/simd_sse2.c: guard the unbound sse_cmpeq8 and its intrinsic includes behind __x86_64__ / __i386__. - CI: add an ubuntu-24.04-arm runner and disambiguate the cabal-cache key by runner.arch (x86_64 and aarch64 Linux otherwise share runner.os). Verified on aarch64: library builds and the hspec/hedgehog test suite passes. --- .github/workflows/haskell.yml | 6 +++--- ChangeLog.md | 8 ++++++++ cbits/simd_avx2.c | 10 ++++++++-- cbits/simd_sse2.c | 10 ++++++++-- hw-simd.cabal | 34 +++++++++++++++++++--------------- 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/.github/workflows/haskell.yml b/.github/workflows/haskell.yml index fb9e30a..5cc8f5c 100644 --- a/.github/workflows/haskell.yml +++ b/.github/workflows/haskell.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: ghc: ["9.12.2", "9.10.2", "9.8.4", "9.6.7"] - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest] env: # Modify this value to "invalidate" the cabal cache. @@ -56,7 +56,7 @@ jobs: dist-dir: dist-newstyle store-path: ${{ steps.setup-haskell.outputs.cabal-store }} threads: 16 - archive-uri: ${{ secrets.BINARY_CACHE_URI }}/${{ env.CABAL_CACHE_VERSION }}/${{ runner.os }}/${{ matrix.cabal }}/${{ matrix.ghc }} + archive-uri: ${{ secrets.BINARY_CACHE_URI }}/${{ env.CABAL_CACHE_VERSION }}/${{ runner.os }}/${{ runner.arch }}/${{ matrix.cabal }}/${{ matrix.ghc }} skip: "${{ secrets.BINARY_CACHE_URI == '' }}" - name: Cabal cache over HTTPS @@ -65,7 +65,7 @@ jobs: dist-dir: dist-newstyle store-path: ${{ steps.setup-haskell.outputs.cabal-store }} threads: 16 - archive-uri: https://cache.haskellworks.io/${{ env.CABAL_CACHE_VERSION }}/${{ runner.os }}/${{ matrix.cabal }}/${{ matrix.ghc }} + archive-uri: https://cache.haskellworks.io/${{ env.CABAL_CACHE_VERSION }}/${{ runner.os }}/${{ runner.arch }}/${{ matrix.cabal }}/${{ matrix.ghc }} skip: "${{ secrets.BINARY_CACHE_URI != '' }}" - name: Build diff --git a/ChangeLog.md b/ChangeLog.md index b71543c..428231b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1 +1,9 @@ # Changelog for hw-simd + +## 0.1.3.0 + +- Support non-x86 architectures (e.g. `aarch64` / `arm`, such as AWS Graviton). + The x86-specific instruction-set flags (`-mavx2`, `-mbmi2`, `-msse4.2`) are now + only emitted for x86 targets, and the x86 intrinsic C code is guarded + accordingly. On non-x86 architectures the library builds and runs using the + pure-Haskell "Stock" implementations. diff --git a/cbits/simd_avx2.c b/cbits/simd_avx2.c index b71790b..dac221e 100644 --- a/cbits/simd_avx2.c +++ b/cbits/simd_avx2.c @@ -1,7 +1,5 @@ #include "simd.h" -#include -#include #include #include #include @@ -9,6 +7,14 @@ #include #include +// The x86 intrinsic headers only exist on x86 targets. Every function body in +// this file is guarded by AVX2_ENABLED (defined only for x86 builds), so on +// other architectures this translation unit compiles to empty no-ops. +#if defined(AVX2_ENABLED) +#include +#include +#endif + void avx2_memcpy( uint8_t *target, uint8_t *source, diff --git a/cbits/simd_sse2.c b/cbits/simd_sse2.c index 5ccd7ad..0dc69f1 100644 --- a/cbits/simd_sse2.c +++ b/cbits/simd_sse2.c @@ -1,7 +1,5 @@ #include "simd.h" -#include -#include #include #include #include @@ -9,6 +7,13 @@ #include #include +// sse_cmpeq8 uses x86 SSE intrinsics and is not bound from Haskell. The x86 +// intrinsic headers only exist on x86 targets, so this whole translation unit +// is compiled to an empty no-op on other architectures (e.g. aarch64 / arm). +#if defined(__x86_64__) || defined(__i386__) +#include +#include + void sse_cmpeq8( uint8_t byte, uint64_t *target, @@ -28,3 +33,4 @@ void sse_cmpeq8( target16[i] = mask; } } +#endif diff --git a/hw-simd.cabal b/hw-simd.cabal index b93a5f7..8ed6eb2 100644 --- a/hw-simd.cabal +++ b/hw-simd.cabal @@ -1,7 +1,7 @@ cabal-version: 2.2 name: hw-simd -version: 0.1.2.2 +version: 0.1.3.0 synopsis: SIMD library description: Please see the README on Github at category: Data, Bit, SIMD @@ -66,22 +66,26 @@ common vector { build-depends: vector >= common config default-language: Haskell2010 ghc-options: -O2 -Wall - if (flag(avx2)) && (impl(ghc >=8.4.1)) - ghc-options: -mbmi2 -msse4.2 - cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED if (impl(ghc >=8.0.1)) ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints - if flag(avx2) - cc-options: -mavx2 -DAVX2_ENABLED - if (flag(bmi2)) && (impl(ghc >=8.4.1)) - ghc-options: -mbmi2 -msse4.2 - if flag(bmi2) - cc-options: -mbmi2 -DBMI2_ENABLED - if flag(sse42) - ghc-options: -msse4.2 - cc-options: -msse4.2 - if (arch(aarch64) || arch(arm)) - build-depends: base < 0 + -- The avx2/bmi2/sse42 instruction sets and their associated compiler flags + -- (-mavx2, -mbmi2, -msse4.2) are x86-specific. Only emit them when building + -- for an x86 target so that the library also builds on other architectures + -- (e.g. aarch64 / arm), where it falls back to the pure-Haskell "Stock" + -- implementations. + if (arch(x86_64) || arch(i386)) + if (flag(avx2)) && (impl(ghc >=8.4.1)) + ghc-options: -mbmi2 -msse4.2 + cpp-options: -DBMI2_ENABLED -DAVX2_ENABLED + if flag(avx2) + cc-options: -mavx2 -DAVX2_ENABLED + if (flag(bmi2)) && (impl(ghc >=8.4.1)) + ghc-options: -mbmi2 -msse4.2 + if flag(bmi2) + cc-options: -mbmi2 -DBMI2_ENABLED + if flag(sse42) + ghc-options: -msse4.2 + cc-options: -msse4.2 common hw-simd build-depends: hw-simd