This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository terminology.
View the commit online.
commit 4b7fc3874e4663d61d09a1ee11bd49867b5c307f
Author: Cedric BAIL <[email protected]>
AuthorDate: Mon Aug 3 13:27:46 2026 -0600
simd: check each vector kernel against its scalar reference
Modelled on EFL's evas_test_neon_ops.c. Buffers are guard-padded, so a kernel
that writes outside its range fails even when the in-range bytes are right.
Every length and alignment around the vector width is walked, since the bugs
live in the tail and at the seam between the vector body and the scalar
remainder. And every byte value is swept at every position, which is cheap
enough here to do exhaustively rather than by sampling.
Run as 'tytest simd_parity'. The harness compiles only where there are two
kernels to compare, so on a target without a vector form it is an empty test
rather than a scalar-against-itself one.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
src/bin/simd/simd.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/bin/tytest.c | 1 +
src/bin/unit_tests.h | 1 +
3 files changed, 147 insertions(+)
diff --git a/src/bin/simd/simd.c b/src/bin/simd/simd.c
index d04987ae..81de04f3 100644
--- a/src/bin/simd/simd.c
+++ b/src/bin/simd/simd.c
@@ -37,3 +37,148 @@ simd_scan_plain_ascii(const unsigned char *buf, size_t len)
#endif
return simd_scan_plain_ascii_scalar(buf, len);
}
+/* Parity tests: each vector kernel must agree with its scalar reference.
+ *
+ * Buffers are guard-padded so a kernel writing outside its range fails even
+ * when the in-range bytes are right, and every length and alignment around the
+ * vector width is walked, since the bugs live in the tail and at the seam
+ * between the vector body and the scalar remainder. */
+#if defined(BINARY_TYTEST)
+#include <assert.h>
+
+/* Only the NEON build has two kernels to compare, so the whole harness --
+ * helpers included -- is compiled only there. */
+#if defined(TERMINOLOGY_HAVE_NEON)
+
+#define GUARD 32
+#define GUARD_BYTE 0xA5
+
+/* Deterministic: a parity failure has to be reproducible to be debuggable. */
+static unsigned int _seed = 0x9e3779b9;
+
+static unsigned int
+_rnd(void)
+{
+ _seed ^= _seed << 13;
+ _seed ^= _seed >> 17;
+ _seed ^= _seed << 5;
+ return _seed;
+}
+
+static unsigned char *
+_alloc_guarded(size_t len)
+{
+ unsigned char *base = malloc(len + 2 * GUARD);
+
+ assert(base != NULL);
+ memset(base, GUARD_BYTE, len + 2 * GUARD);
+ return base;
+}
+
+static Eina_Bool
+_guards_intact(const unsigned char *base, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < GUARD; i++)
+ {
+ if (base[i] != GUARD_BYTE)
+ return EINA_FALSE;
+ }
+ for (i = 0; i < GUARD; i++)
+ {
+ if (base[GUARD + len + i] != GUARD_BYTE)
+ return EINA_FALSE;
+ }
+ return EINA_TRUE;
+}
+
+/* Mostly printable ASCII so runs reach the vector body, salted with the exact
+ * boundary values the kernels test against. */
+static void
+_fill(unsigned char *p, size_t len, int density)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ {
+ if ((int)(_rnd() % 100) < density)
+ {
+ switch (_rnd() % 6)
+ {
+ case 0: p[i] = 0x00; break;
+ case 1: p[i] = 0x1f; break;
+ case 2: p[i] = 0x7f; break;
+ case 3: p[i] = 0x80; break;
+ case 4: p[i] = 0xff; break;
+ default: p[i] = (unsigned char)(_rnd() % 0x20); break;
+ }
+ }
+ else
+ p[i] = (unsigned char)(0x20 + (_rnd() % 0x5f));
+ }
+}
+
+static void
+_test_scan(void)
+{
+ size_t len, off;
+ int density;
+
+ for (len = 0; len <= 70; len++)
+ {
+ for (density = 0; density <= 100; density += 10)
+ {
+ for (off = 0; off < 16; off++)
+ {
+ unsigned char *base = _alloc_guarded(off + len);
+ unsigned char *p = base + GUARD + off;
+
+ _fill(p, len, density);
+ assert(simd_scan_plain_ascii_scalar(p, len) ==
+ simd_scan_plain_ascii_neon(p, len));
+ assert(_guards_intact(base, off + len));
+ free(base);
+ }
+ }
+ }
+}
+
+/* Every byte value, at every position, exhaustively. */
+static void
+_test_every_byte(void)
+{
+ unsigned int v;
+ size_t len, pos;
+
+ for (v = 0; v < 256; v++)
+ {
+ for (len = 1; len <= 40; len++)
+ {
+ for (pos = 0; pos < len; pos++)
+ {
+ unsigned char buf[64];
+
+ memset(buf, 'x', sizeof(buf));
+ buf[pos] = (unsigned char)v;
+ assert(simd_scan_plain_ascii_scalar(buf, len) ==
+ simd_scan_plain_ascii_neon(buf, len));
+ }
+ }
+ }
+}
+
+#endif
+
+int
+tytest_simd_parity(void)
+{
+#if defined(TERMINOLOGY_HAVE_NEON)
+ _test_scan();
+ _test_every_byte();
+#endif
+ /* Without a vector kernel the scalar path is the only path. */
+ return 0;
+}
+
+#endif
diff --git a/src/bin/tytest.c b/src/bin/tytest.c
index 15c9eda6..23db0a19 100644
--- a/src/bin/tytest.c
+++ b/src/bin/tytest.c
@@ -32,6 +32,7 @@ static struct {
tytest_func func;
} _tytests[] = {
{ "dummy", tytest_dummy },
+ { "simd_parity", tytest_simd_parity},
{ "sb_skip", tytest_sb_skip},
{ "sb_trim", tytest_sb_trim},
{ "sb_gap", tytest_sb_gap},
diff --git a/src/bin/unit_tests.h b/src/bin/unit_tests.h
index aa498069..4743d168 100644
--- a/src/bin/unit_tests.h
+++ b/src/bin/unit_tests.h
@@ -6,6 +6,7 @@ typedef int (*tytest_func)(void);
/* list of tests */
int tytest_dummy(void);
+int tytest_simd_parity(void);
int tytest_sb_skip(void);
int tytest_sb_trim(void);
int tytest_sb_gap(void);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.