keccak-fast.c

Minimal SIMD keccak implementation
git clone git://git.finwo.net/lib/keccak-fast.c
Log | Files | Refs | README | LICENSE

commit d5efc1d3ff93f16f9ee6f5c2862ffd33c21eb1f8
parent 721000be4b2df7f3d084e9dce86bb229036393dc
Author: finwo <finwo@pm.me>
Date:   Sat, 10 Oct 2026 16:16:03 +0200

Add avx512 backend

Diffstat:
MREADME.md | 5+++--
Mbench/bench.c | 4++--
Mexport.mk | 1+
Asrc/backend/avx512.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md @@ -33,11 +33,12 @@ dep add finwo/keccak-fast | scalar | yes | — | | scalar+bmi | yes | — | | AVX2 | no | x4 | +| AVX-512 | no | x8 | Backends register from constructors; the highest available priority wins (scalar 0, scalar+bmi 10, avx2 20, avx512 30). `kf_backend_name()` and -`kf_batch_name()` report the active ones. `KECCAK_FAST_NO_BMI` and -`KECCAK_FAST_NO_AVX2` drop those backends. +`kf_batch_name()` report the active ones. `KECCAK_FAST_NO_BMI`, +`KECCAK_FAST_NO_AVX2` and `KECCAK_FAST_NO_AVX512` drop those backends. The scalar backend is the donor, pinned to the x86-64 baseline (`no-avx,no-avx2,no-avx512f,no-bmi,no-bmi2`) so a consumer's `-march=native` diff --git a/bench/bench.c b/bench/bench.c @@ -134,7 +134,7 @@ int main(void) { for (size_t i = 0; i < sizeof bin; i++) bin[i] = (uint8_t)(i * 17 + 3); printf("batch SHAKE256 32->32, per hash (%s)\n", kf_batch_name()); - case_batch(kf_batch_name(), kf_shake256_batch, 4, 32, 32, bin, bout, reps, + case_batch(kf_batch_name(), kf_shake256_batch, 8, 32, 32, bin, bout, reps, target_ns); case_batch(kf_batch_name(), kf_shake256_batch, 64, 32, 32, bin, bout, reps, target_ns); @@ -142,7 +142,7 @@ int main(void) { target_ns); printf("batch SHA3-256 32->32, per hash (%s)\n", kf_batch_name()); - case_batch(kf_batch_name(), kf_sha3_256_batch, 4, 32, 32, bin, bout, reps, + case_batch(kf_batch_name(), kf_sha3_256_batch, 8, 32, 32, bin, bout, reps, target_ns); case_batch(kf_batch_name(), kf_sha3_256_batch, 64, 32, 32, bin, bout, reps, target_ns); diff --git a/export.mk b/export.mk @@ -2,3 +2,4 @@ SRC+={{module.dirname}}/src/keccak-fast.c SRC+={{module.dirname}}/src/backend/scalar.c SRC+={{module.dirname}}/src/backend/scalar_bmi.c SRC+={{module.dirname}}/src/backend/avx2.c +SRC+={{module.dirname}}/src/backend/avx512.c diff --git a/src/backend/avx512.c b/src/backend/avx512.c @@ -0,0 +1,121 @@ +#include <immintrin.h> +#include <stddef.h> +#include <stdint.h> + +#include "../keccak-fast-internal.h" + +#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__)) && \ + !defined(KECCAK_FAST_NO_AVX512) + +static const uint64_t RC[24] = { + 1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, + 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, + 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; + +__attribute__((target("avx512f"))) +static inline void kf_avx512_round_x8(__m512i v[25], uint64_t rc) { + __m512i c[5]; + __m512i d[5]; + __m512i t, u; + + for (int x = 0; x < 5; x++) { + c[x] = _mm512_ternarylogic_epi64(v[x], v[x + 5], v[x + 10], 0x96); + c[x] = _mm512_ternarylogic_epi64(c[x], v[x + 15], v[x + 20], 0x96); + } + for (int x = 0; x < 5; x++) { + d[x] = _mm512_xor_si512(c[(x + 4) % 5], _mm512_rol_epi64(c[(x + 1) % 5], 1)); + } + for (int y = 0; y < 5; y++) { + for (int x = 0; x < 5; x++) { + v[x + 5 * y] = _mm512_xor_si512(v[x + 5 * y], d[x]); + } + } + + t = v[1]; +#define KF_RHOPI8(dest, rot) \ + do { \ + u = v[dest]; \ + v[dest] = _mm512_rol_epi64(t, rot); \ + t = u; \ + } while (0) + KF_RHOPI8(10, 1); + KF_RHOPI8(7, 3); + KF_RHOPI8(11, 6); + KF_RHOPI8(17, 10); + KF_RHOPI8(18, 15); + KF_RHOPI8(3, 21); + KF_RHOPI8(5, 28); + KF_RHOPI8(16, 36); + KF_RHOPI8(8, 45); + KF_RHOPI8(21, 55); + KF_RHOPI8(24, 2); + KF_RHOPI8(4, 14); + KF_RHOPI8(15, 27); + KF_RHOPI8(23, 41); + KF_RHOPI8(19, 56); + KF_RHOPI8(13, 8); + KF_RHOPI8(12, 25); + KF_RHOPI8(2, 43); + KF_RHOPI8(20, 62); + KF_RHOPI8(14, 18); + KF_RHOPI8(22, 39); + KF_RHOPI8(9, 61); + KF_RHOPI8(6, 20); + KF_RHOPI8(1, 44); +#undef KF_RHOPI8 + + for (int y = 0; y < 5; y++) { + __m512i row[5]; + for (int x = 0; x < 5; x++) row[x] = v[x + 5 * y]; + for (int x = 0; x < 5; x++) { + v[x + 5 * y] = + _mm512_ternarylogic_epi64(row[x], row[(x + 1) % 5], row[(x + 2) % 5], 0xD2); + } + } + + v[0] = _mm512_xor_si512(v[0], _mm512_set1_epi64((long long)rc)); +} + +__attribute__((target("avx512f"))) +static void kf_permute_x8(uint64_t s[8][25]) { + __m512i v[25]; + uint64_t tmp[8]; + + for (int j = 0; j < 25; j++) { + for (int i = 0; i < 8; i++) tmp[i] = s[i][j]; + v[j] = _mm512_loadu_si512((const __m512i *)tmp); + } + for (int i = 0; i < 24; i++) kf_avx512_round_x8(v, RC[i]); + for (int j = 0; j < 25; j++) { + _mm512_storeu_si512((__m512i *)tmp, v[j]); + for (int i = 0; i < 8; i++) s[i][j] = tmp[i]; + } +} + +__attribute__((target("avx512f"))) +static void kf_avx512_batch(uint64_t states[][25], size_t count) { + size_t i = 0; + for (; i + 8 <= count; i += 8) { + kf_permute_x8(&states[i]); + } + for (; i < count; i++) { + kf_scalar_permute(states[i]); + } +} + +__attribute__((constructor)) static void kf_avx512_register(void) { + if (!__builtin_cpu_supports("avx512f")) { + return; + } + if (kf_batch_priority <= KF_PRIO_AVX512) { + kf_batch_perm = kf_avx512_batch; + kf_batch_priority = KF_PRIO_AVX512; + kf_batch_lanes = 8; + kf_batch_label = "avx512"; + } +} + +#endif