From 7797e5fdf062c49bf95b7b8e27fee95a82fa7d3a Mon Sep 17 00:00:00 2001 From: Sahana Bogar Date: Fri, 26 Jun 2026 01:16:59 +0530 Subject: [PATCH 1/2] load neon vint4 bytes via memcpy to avoid unaligned aliasing --- Source/astcenc_vecmathlib_neon_4.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/astcenc_vecmathlib_neon_4.h b/Source/astcenc_vecmathlib_neon_4.h index 73d47dc9..7be4c23e 100644 --- a/Source/astcenc_vecmathlib_neon_4.h +++ b/Source/astcenc_vecmathlib_neon_4.h @@ -195,8 +195,12 @@ struct vint4 ASTCENC_SIMD_INLINE explicit vint4(const uint8_t *p) { #if ASTCENC_SVE == 0 - // Cast is safe - NEON loads are allowed to be unaligned - uint32x2_t t8 = vld1_dup_u32(reinterpret_cast(p)); + // Copy through a uint32_t to avoid a load through a reinterpreted pointer, + // which is both unaligned (p has no four byte alignment guarantee) and an + // aliasing violation. Matches the SSE and AVX2 backends. + uint32_t tmp; + std::memcpy(&tmp, p, sizeof(tmp)); + uint32x2_t t8 = vdup_n_u32(tmp); uint16x4_t t16 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(t8))); m = vreinterpretq_s32_u32(vmovl_u16(t16)); #else From f3037a213724b98813b01dc7765f689535683082 Mon Sep 17 00:00:00 2001 From: Peter Harris Date: Fri, 26 Jun 2026 13:23:11 +0100 Subject: [PATCH 2/2] Standardize comments across SIMD backends --- Source/astcenc_vecmathlib_avx2_8.h | 7 ++++--- Source/astcenc_vecmathlib_neon_4.h | 20 ++++++++++---------- Source/astcenc_vecmathlib_sse_4.h | 7 ++++--- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Source/astcenc_vecmathlib_avx2_8.h b/Source/astcenc_vecmathlib_avx2_8.h index 7cd34c31..17147a14 100644 --- a/Source/astcenc_vecmathlib_avx2_8.h +++ b/Source/astcenc_vecmathlib_avx2_8.h @@ -142,12 +142,13 @@ struct vint8 */ ASTCENC_SIMD_INLINE explicit vint8(const uint8_t *p) { - // _mm_loadu_si64 would be nicer syntax, but missing on older GCC and - // generates broken code on GCC 11.x before 11.3, so use this to - // generate alignment-safe and aliasing-safe alternative + // Copy through a uint32_t to avoid load through a reinterpreted + // pointer, which is both unaligned and an aliasing violation. uint64_t tmp; std::memcpy(&tmp, p, sizeof(tmp)); + // _mm_loadu_si64 would be nicer syntax, but missing on older GCC and + // generates broken code on GCC 11.x before 11.3. m = _mm256_cvtepu8_epi32(_mm_cvtsi64_si128(tmp)); } diff --git a/Source/astcenc_vecmathlib_neon_4.h b/Source/astcenc_vecmathlib_neon_4.h index 7be4c23e..9086b8b1 100644 --- a/Source/astcenc_vecmathlib_neon_4.h +++ b/Source/astcenc_vecmathlib_neon_4.h @@ -195,17 +195,17 @@ struct vint4 ASTCENC_SIMD_INLINE explicit vint4(const uint8_t *p) { #if ASTCENC_SVE == 0 - // Copy through a uint32_t to avoid a load through a reinterpreted pointer, - // which is both unaligned (p has no four byte alignment guarantee) and an - // aliasing violation. Matches the SSE and AVX2 backends. - uint32_t tmp; - std::memcpy(&tmp, p, sizeof(tmp)); - uint32x2_t t8 = vdup_n_u32(tmp); - uint16x4_t t16 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(t8))); - m = vreinterpretq_s32_u32(vmovl_u16(t16)); + // Copy through a uint32_t to avoid load through a reinterpreted + // pointer, which is both unaligned and an aliasing violation. + uint32_t tmp; + std::memcpy(&tmp, p, sizeof(tmp)); + + uint32x2_t t8 = vdup_n_u32(tmp); + uint16x4_t t16 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(t8))); + m = vreinterpretq_s32_u32(vmovl_u16(t16)); #else - svint32_t data = svld1ub_s32(svptrue_pat_b32(SV_VL4), p); - m = svget_neonq(data); + svint32_t data = svld1ub_s32(svptrue_pat_b32(SV_VL4), p); + m = svget_neonq(data); #endif } diff --git a/Source/astcenc_vecmathlib_sse_4.h b/Source/astcenc_vecmathlib_sse_4.h index 823b41a9..c0eb6553 100644 --- a/Source/astcenc_vecmathlib_sse_4.h +++ b/Source/astcenc_vecmathlib_sse_4.h @@ -207,12 +207,13 @@ struct vint4 */ ASTCENC_SIMD_INLINE explicit vint4(const uint8_t *p) { - // _mm_loadu_si32 would be nicer syntax, but missing on older GCC and - // generates broken code on GCC 11.x before 11.3, so use this to - // generate alignment-safe and aliasing-safe alternative + // Copy through a uint32_t to avoid load through a reinterpreted + // pointer, which is both unaligned and an aliasing violation. int32_t tmp; std::memcpy(&tmp, p, sizeof(tmp)); + // _mm_loadu_si32 would be nicer syntax, but missing on older GCC and + // generates broken code on GCC 11.x before 11.3. __m128i t = _mm_cvtsi32_si128(tmp); #if ASTCENC_SSE >= 41