commit 6e0b22f061e5bdff78da508aff4ee919feafec9a
parent 03b6dbe88aead7c7d2f2d32e9caf00e450d3ee38
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 31 Oct 2026 14:28:16 +0400
cbits: use the scalar kernel on x86-64
The scalar 26-bit-limb kernel is portable C, so compile it on x86-64
as well as aarch64, and have poly1305_native_available report it
there. The NEON 4-way kernel remains aarch64-only; on x86-64 every
block goes through the scalar kernel.
On an x86-64 AMD EPYC 7642 host, with the LLVM backend, versus the
pure Haskell implementation:
mac (114B msg): 347 ns -> 234 ns
mac (1024B msg): 2.33 us -> 1.26 us
mac (4096B msg): 8.35 us -> 4.84 us
Diffstat:
2 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/cbits/poly1305_native.c b/cbits/poly1305_native.c
@@ -3,20 +3,23 @@
#include <string.h>
#if defined(__aarch64__)
-
#include <arm_neon.h>
+#endif
+
+#if defined(__aarch64__) || defined(__x86_64__)
/*
- * Poly1305 (RFC 8439). ARM acceleration via two paths:
+ * Poly1305 (RFC 8439), via two paths:
*
- * 1. A scalar 26-bit-limb kernel using 64-bit native arithmetic for
- * the limb multiplications. Used for setup (precomputing r^k),
+ * 1. A portable scalar 26-bit-limb kernel using 64-bit native
+ * arithmetic for the limb multiplications, compiled on both
+ * aarch64 and x86_64. Used for setup (precomputing r^k),
* for messages shorter than 64 bytes, and for the < 4-block tail
* of longer messages. Also serves as the stage-1 reference; the
* NEON kernel below was added in a separate commit.
*
- * 2. A NEON 4-way parallel kernel (when at least 4 full blocks
- * remain). Layout: 5 'uint32x4_t' limb vectors hold one limb
+ * 2. On aarch64, a NEON 4-way parallel kernel (when at least 4 full
+ * blocks remain). Layout: 5 'uint32x4_t' limb vectors hold one limb
* position each across 4 message blocks; matching r-power
* vectors hold (r^4, r^3, r^2, r^1) at the same limb position.
* For each output position d_j we accumulate 5 partial products
@@ -109,6 +112,8 @@ static inline void scalar_block(uint32_t h[5], const uint32_t r[5],
mul_mod_p(hl, r, h);
}
+#if defined(__aarch64__)
+
/*
* 4-block NEON update. Computes:
* h := (h + m_0)*r^4 + m_1*r^3 + m_2*r^2 + m_3*r^1 mod p
@@ -226,6 +231,8 @@ static void neon4_block(uint32_t h[5],
h[4] = (uint32_t)d4;
}
+#endif
+
/*
* Compute a 16-byte Poly1305 MAC over 'msg' (length 'msglen') using
* the 32-byte 'key'. Writes the tag to 'mac_out'.
@@ -254,6 +261,7 @@ void poly1305_mac_native(const uint8_t key[32], const uint8_t *msg,
size_t pos = 0;
+#if defined(__aarch64__)
/* NEON 4-way path: amortizing the r^2/r^3/r^4 precomputation
* (3 scalar mul_mod_p calls) needs about 4 NEON iterations to
* break even versus the scalar block loop, so only engage it
@@ -269,6 +277,7 @@ void poly1305_mac_native(const uint8_t key[32], const uint8_t *msg,
pos += 64;
}
}
+#endif
/* Scalar tail: any remaining full blocks (< 4 of them). */
while (pos + 16 <= msglen) {
diff --git a/lib/Crypto/MAC/Poly1305/Native.hs b/lib/Crypto/MAC/Poly1305/Native.hs
@@ -7,7 +7,8 @@
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
--- ARM acceleration for the Poly1305 MAC.
+-- A native C kernel for the Poly1305 MAC on aarch64 and x86_64, with
+-- ARM NEON acceleration.
module Crypto.MAC.Poly1305.Native (
poly1305_native_available
@@ -39,7 +40,7 @@ fi = fromIntegral
-- api ------------------------------------------------------------------------
--- | Are ARM extensions available?
+-- | Is the native kernel available?
poly1305_native_available :: Bool
poly1305_native_available =
unsafeDupablePerformIO c_poly1305_native_available /= 0