From c9a0a93c62727ff1b1ec30e45fdd6fb10c747192 Mon Sep 17 00:00:00 2001 From: gabewillen Date: Fri, 7 Aug 2026 02:19:46 -0500 Subject: [PATCH] fix(sqlite-vec): preserve signed NEON L1 differences --- third_party/sqlite-vec/sqlite-vec.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/third_party/sqlite-vec/sqlite-vec.c b/third_party/sqlite-vec/sqlite-vec.c index fe6090c2..eb467470 100644 --- a/third_party/sqlite-vec/sqlite-vec.c +++ b/third_party/sqlite-vec/sqlite-vec.c @@ -263,6 +263,15 @@ static f32 l2_sqr_int8_neon(const void *pVect1v, const void *pVect2v, return sqrtf(sum_scalar); } +// Keep the byte-wise absolute differences unsigned while widening. The +// signed vabdq_s8 result is bit-reinterpreted so differences above INT8_MAX +// retain their 0..255 magnitude; the final reinterpret supplies vaddq_s32's +// signed lane type without changing those lane bits. +static inline int32x4_t l1_int8_neon_pairwise_abs(int8x16_t diff) { + return vreinterpretq_s32_u32( + vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(diff)))); +} + static i32 l1_int8_neon(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { i8 *pVect1 = (i8 *)pVect1v; @@ -280,25 +289,22 @@ static i32 l1_int8_neon(const void *pVect1v, const void *pVect2v, int8x16_t v1 = vld1q_s8(pVect1); int8x16_t v2 = vld1q_s8(pVect2); int8x16_t diff1 = vabdq_s8(v1, v2); - // vabdq_s8 returns signed vector bits; reinterpret them as unsigned - // before widening so absolute differences in the 128..255 range are - // accumulated without sign extension. - acc1 = vaddq_s32(acc1, vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(diff1)))); + acc1 = vaddq_s32(acc1, l1_int8_neon_pairwise_abs(diff1)); v1 = vld1q_s8(pVect1 + 16); v2 = vld1q_s8(pVect2 + 16); int8x16_t diff2 = vabdq_s8(v1, v2); - acc2 = vaddq_s32(acc2, vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(diff2)))); + acc2 = vaddq_s32(acc2, l1_int8_neon_pairwise_abs(diff2)); v1 = vld1q_s8(pVect1 + 32); v2 = vld1q_s8(pVect2 + 32); int8x16_t diff3 = vabdq_s8(v1, v2); - acc3 = vaddq_s32(acc3, vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(diff3)))); + acc3 = vaddq_s32(acc3, l1_int8_neon_pairwise_abs(diff3)); v1 = vld1q_s8(pVect1 + 48); v2 = vld1q_s8(pVect2 + 48); int8x16_t diff4 = vabdq_s8(v1, v2); - acc4 = vaddq_s32(acc4, vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(diff4)))); + acc4 = vaddq_s32(acc4, l1_int8_neon_pairwise_abs(diff4)); pVect1 += 64; pVect2 += 64; @@ -308,7 +314,8 @@ static i32 l1_int8_neon(const void *pVect1v, const void *pVect2v, int8x16_t v1 = vld1q_s8(pVect1); int8x16_t v2 = vld1q_s8(pVect2); int8x16_t diff = vabdq_s8(v1, v2); - acc1 = vaddq_s32(acc1, vpaddlq_u16(vpaddlq_u8(vreinterpretq_u8_s8(diff)))); + acc1 = vaddq_s32(acc1, l1_int8_neon_pairwise_abs(diff)); + pVect1 += 16; pVect2 += 16; }