From dd261e277506d77c29a7ee89d5b6c0a74ad6fc90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sat, 17 Oct 2026 17:20:12 -0700 Subject: [PATCH] Added simd_i8_cmpeq for all SIMD widths. Fixed BitArray kSimdByteMask shift for 256-bit SIMD. (#466) --- include/bx/inline/simd128_neon.inl | 9 +++++++++ include/bx/inline/simd128_ref.inl | 21 +++++++++++++++++++++ include/bx/inline/simd128_sse.inl | 9 +++++++++ include/bx/inline/simd128_wasm.inl | 6 ++++++ include/bx/inline/simd256_avx.inl | 30 ++++++++++++++++++++++++++++++ include/bx/inline/simd256_ref.inl | 9 +++++++++ include/bx/inline/simd32_ref.inl | 15 +++++++++++++++ include/bx/inline/simd64_ref.inl | 15 +++++++++++++++ include/bx/inline/simd_impl.inl | 15 +++++++++++++++ include/bx/simd_t.h | 12 ++++++++++++ src/bitarray.cpp | 2 +- tests/simd_test.cpp | 14 ++++++++++++++ 12 files changed, 156 insertions(+), 1 deletion(-) diff --git a/include/bx/inline/simd128_neon.inl b/include/bx/inline/simd128_neon.inl index 168da07..9eacf3a 100644 --- a/include/bx/inline/simd128_neon.inl +++ b/include/bx/inline/simd128_neon.inl @@ -750,6 +750,15 @@ namespace bx return vreinterpretq_f32_s8(result); } + template<> + BX_SIMD_FORCE_INLINE simd128_neon_t simd128_i8_cmpeq(simd128_neon_t _a, simd128_neon_t _b) + { + const int8x16_t a = vreinterpretq_s8_f32(_a); + const int8x16_t b = vreinterpretq_s8_f32(_b); + const uint8x16_t result = vceqq_s8(a, b); + return vreinterpretq_f32_u8(result); + } + template<> BX_SIMD_FORCE_INLINE simd128_neon_t simd128_u8_satadd(simd128_neon_t _a, simd128_neon_t _b) { diff --git a/include/bx/inline/simd128_ref.inl b/include/bx/inline/simd128_ref.inl index 1b986ac..0194c86 100644 --- a/include/bx/inline/simd128_ref.inl +++ b/include/bx/inline/simd128_ref.inl @@ -1136,6 +1136,27 @@ namespace bx #endif // BX_SIMD_LANGEXT } + template<> + inline BX_CONSTEXPR_FUNC simd128_ref_t simd128_i8_cmpeq(simd128_ref_t _a, simd128_ref_t _b) + { +#if BX_SIMD_LANGEXT + const simd128_i8_langext_t a = bitCast(_a); + const simd128_i8_langext_t b = bitCast(_b); + const auto mask = a == b; + const simd128_ref_t result = bitCast(mask); + return result; +#else + const simd128_u8_ref_t a = bitCast(_a); + const simd128_u8_ref_t b = bitCast(_b); + simd128_u8_ref_t result; + for (int ii = 0; ii < 16; ++ii) + { + result.u8[ii] = a.u8[ii] == b.u8[ii] ? 0xff : 0; + } + return bitCast(result); +#endif // BX_SIMD_LANGEXT + } + template<> inline BX_CONSTEXPR_FUNC simd128_ref_t simd128_u8_satadd(simd128_ref_t _a, simd128_ref_t _b) { diff --git a/include/bx/inline/simd128_sse.inl b/include/bx/inline/simd128_sse.inl index 25d98a3..d81802e 100644 --- a/include/bx/inline/simd128_sse.inl +++ b/include/bx/inline/simd128_sse.inl @@ -760,6 +760,15 @@ namespace bx return _mm_castsi128_ps(result); } + template<> + BX_SIMD_FORCE_INLINE simd128_sse_t simd128_i8_cmpeq(simd128_sse_t _a, simd128_sse_t _b) + { + const __m128i a = _mm_castps_si128(_a); + const __m128i b = _mm_castps_si128(_b); + const __m128i result = _mm_cmpeq_epi8(a, b); + return _mm_castsi128_ps(result); + } + template<> BX_SIMD_FORCE_INLINE simd128_sse_t simd128_u8_satadd(simd128_sse_t _a, simd128_sse_t _b) { diff --git a/include/bx/inline/simd128_wasm.inl b/include/bx/inline/simd128_wasm.inl index f34e1f1..6c2ce21 100644 --- a/include/bx/inline/simd128_wasm.inl +++ b/include/bx/inline/simd128_wasm.inl @@ -608,6 +608,12 @@ namespace bx return wasm_i8x16_sub(_a, _b); } + template<> + BX_SIMD_FORCE_INLINE simd128_wasm_t simd128_i8_cmpeq(simd128_wasm_t _a, simd128_wasm_t _b) + { + return wasm_i8x16_eq(_a, _b); + } + template<> BX_SIMD_FORCE_INLINE simd128_wasm_t simd128_u8_satadd(simd128_wasm_t _a, simd128_wasm_t _b) { diff --git a/include/bx/inline/simd256_avx.inl b/include/bx/inline/simd256_avx.inl index 9b009e8..5d3dc8b 100644 --- a/include/bx/inline/simd256_avx.inl +++ b/include/bx/inline/simd256_avx.inl @@ -746,6 +746,16 @@ namespace bx return result; } + template<> + BX_SIMD_FORCE_INLINE simd256_avx_t simd256_i8_cmpeq(simd256_avx_t _a, simd256_avx_t _b) + { + const __m256i a = _mm256_castps_si256(_a); + const __m256i b = _mm256_castps_si256(_b); + const __m256i cmp = _mm256_cmpeq_epi8(a, b); + const __m256 result = _mm256_castsi256_ps(cmp); + return result; + } + template<> BX_SIMD_FORCE_INLINE simd256_avx_t simd256_u8_satadd(simd256_avx_t _a, simd256_avx_t _b) { @@ -1420,6 +1430,26 @@ namespace bx return result; } + template<> + BX_SIMD_FORCE_INLINE simd256_avx_t simd256_i8_cmpeq(simd256_avx_t _a, simd256_avx_t _b) + { + const __m128 alo = _mm256_castps256_ps128(_a); + const __m128 ahi = _mm256_extractf128_ps(_a, 1); + const __m128 blo = _mm256_castps256_ps128(_b); + const __m128 bhi = _mm256_extractf128_ps(_b, 1); + const __m128i aloi = _mm_castps_si128(alo); + const __m128i ahii = _mm_castps_si128(ahi); + const __m128i bloi = _mm_castps_si128(blo); + const __m128i bhii = _mm_castps_si128(bhi); + const __m128i cmplo = _mm_cmpeq_epi8(aloi, bloi); + const __m128i cmphi = _mm_cmpeq_epi8(ahii, bhii); + const __m128 rlo = _mm_castsi128_ps(cmplo); + const __m128 rhi = _mm_castsi128_ps(cmphi); + const __m256 rlo256 = _mm256_castps128_ps256(rlo); + const __m256 result = _mm256_insertf128_ps(rlo256, rhi, 1); + return result; + } + template<> BX_SIMD_FORCE_INLINE simd256_avx_t simd256_u8_satadd(simd256_avx_t _a, simd256_avx_t _b) { diff --git a/include/bx/inline/simd256_ref.inl b/include/bx/inline/simd256_ref.inl index 54e36a0..b76ca86 100644 --- a/include/bx/inline/simd256_ref.inl +++ b/include/bx/inline/simd256_ref.inl @@ -1513,6 +1513,15 @@ namespace bx #endif // BX_SIMD256_LANGEXT } + template<> + inline BX_CONSTEXPR_FUNC simd256_ref_t simd256_i8_cmpeq(simd256_ref_t _a, simd256_ref_t _b) + { + simd256_ref_t result; + result.lo = simd128_i8_cmpeq(_a.lo, _b.lo); + result.hi = simd128_i8_cmpeq(_a.hi, _b.hi); + return result; + } + template<> inline BX_CONSTEXPR_FUNC simd256_ref_t simd256_u8_satadd(simd256_ref_t _a, simd256_ref_t _b) { diff --git a/include/bx/inline/simd32_ref.inl b/include/bx/inline/simd32_ref.inl index fa42f55..a900b65 100644 --- a/include/bx/inline/simd32_ref.inl +++ b/include/bx/inline/simd32_ref.inl @@ -571,6 +571,21 @@ BX_PRAGMA_DIAGNOSTIC_POP(); | (uint32_t(uint8_t(int8_t(_a.u32 >> 24) - int8_t(_b.u32 >> 24) ) ) << 24) }; } + inline BX_CONSTEXPR_FUNC simd32_t simd32_i8_cmpeq(simd32_t _a, simd32_t _b) + { + const simd32_t mask7 = simd32_splat(uint8_t(0x7f) ); + const simd32_t diff = simd32_xor(_a, _b); + const simd32_t low7 = simd32_and(diff, mask7); + const simd32_t carry = simd32_u32_add(low7, mask7); + const simd32_t tmp0 = simd32_or(carry, diff); + const simd32_t nonZero = simd32_or(tmp0, mask7); + const simd32_t msbs = simd32_not(nonZero); + const simd32_t lsbs = simd32_x32_srl(msbs, 7); + const simd32_t low7s = simd32_u32_sub(msbs, lsbs); + const simd32_t result = simd32_or(msbs, low7s); + return result; + } + inline BX_CONSTEXPR_FUNC simd32_t simd32_u8_satadd(simd32_t _a, simd32_t _b) { simd32_t result{ .u32 = 0 }; diff --git a/include/bx/inline/simd64_ref.inl b/include/bx/inline/simd64_ref.inl index 96220e1..fb13a27 100644 --- a/include/bx/inline/simd64_ref.inl +++ b/include/bx/inline/simd64_ref.inl @@ -979,6 +979,21 @@ namespace bx return result; } + inline BX_CONSTEXPR_FUNC simd64_t simd64_i8_cmpeq(simd64_t _a, simd64_t _b) + { + const simd64_t mask7 = simd64_splat(uint8_t(0x7f) ); + const simd64_t diff = simd64_xor(_a, _b); + const simd64_t low7 = simd64_and(diff, mask7); + const simd64_t carry = simd64_u64_add(low7, mask7); + const simd64_t tmp0 = simd64_or(carry, diff); + const simd64_t nonZero = simd64_or(tmp0, mask7); + const simd64_t msbs = simd64_not(nonZero); + const simd64_t lsbs = simd64_x64_srl(msbs, 7); + const simd64_t low7s = simd64_u64_sub(msbs, lsbs); + const simd64_t result = simd64_or(msbs, low7s); + return result; + } + inline BX_CONSTEXPR_FUNC simd64_t simd64_u8_satadd(simd64_t _a, simd64_t _b) { simd64_t result{ .u64 = 0 }; diff --git a/include/bx/inline/simd_impl.inl b/include/bx/inline/simd_impl.inl index c5a78b0..5f0b843 100644 --- a/include/bx/inline/simd_impl.inl +++ b/include/bx/inline/simd_impl.inl @@ -93,6 +93,7 @@ namespace bx BX_CONSTEXPR_FUNC simd32_t simd32_x16_srl(simd32_t _a, int _count); BX_CONSTEXPR_FUNC simd32_t simd32_i8_add(simd32_t _a, simd32_t _b); BX_CONSTEXPR_FUNC simd32_t simd32_i8_sub(simd32_t _a, simd32_t _b); + BX_CONSTEXPR_FUNC simd32_t simd32_i8_cmpeq(simd32_t _a, simd32_t _b); BX_CONSTEXPR_FUNC simd32_t simd32_u8_satadd(simd32_t _a, simd32_t _b); BX_CONSTEXPR_FUNC simd32_t simd32_u8_satsub(simd32_t _a, simd32_t _b); BX_CONSTEXPR_FUNC simd32_t simd32_u16_satadd(simd32_t _a, simd32_t _b); @@ -223,6 +224,7 @@ namespace bx BX_CONSTEXPR_FUNC simd64_t simd64_x16_srl(simd64_t _a, int _count); BX_CONSTEXPR_FUNC simd64_t simd64_i8_add(simd64_t _a, simd64_t _b); BX_CONSTEXPR_FUNC simd64_t simd64_i8_sub(simd64_t _a, simd64_t _b); + BX_CONSTEXPR_FUNC simd64_t simd64_i8_cmpeq(simd64_t _a, simd64_t _b); BX_CONSTEXPR_FUNC simd64_t simd64_u8_satadd(simd64_t _a, simd64_t _b); BX_CONSTEXPR_FUNC simd64_t simd64_u8_satsub(simd64_t _a, simd64_t _b); BX_CONSTEXPR_FUNC simd64_t simd64_u16_satadd(simd64_t _a, simd64_t _b); @@ -728,6 +730,10 @@ namespace bx template Ty simd128_u8_satadd(Ty _a, Ty _b); + /// + template + Ty simd128_i8_cmpeq(Ty _a, Ty _b); + /// 16xu8 saturating subtract. /// template @@ -1067,6 +1073,7 @@ namespace bx template Ty simd256_i8_add(Ty _a, Ty _b); template Ty simd256_i8_sub(Ty _a, Ty _b); + template Ty simd256_i8_cmpeq(Ty _a, Ty _b); template Ty simd256_u8_satadd(Ty _a, Ty _b); template Ty simd256_u8_satsub(Ty _a, Ty _b); template Ty simd256_u16_satadd(Ty _a, Ty _b); @@ -1953,6 +1960,8 @@ namespace bx template<> BX_SIMD_FORCE_INLINE simd128_t simd_i32_mul(simd128_t _a, simd128_t _b) { return simd128_u32_mul(_a, _b); } + template<> + BX_SIMD_FORCE_INLINE simd128_t simd_i8_cmpeq(simd128_t _a, simd128_t _b) { return simd128_i8_cmpeq(_a, _b); } template<> BX_SIMD_FORCE_INLINE simd128_t simd_u8_satadd(simd128_t _a, simd128_t _b) { return simd128_u8_satadd(_a, _b); } @@ -2366,6 +2375,8 @@ namespace bx template<> BX_SIMD_FORCE_INLINE simd256_t simd_i32_mul(simd256_t _a, simd256_t _b) { return simd256_u32_mul(_a, _b); } + template<> + BX_SIMD_FORCE_INLINE simd256_t simd_i8_cmpeq(simd256_t _a, simd256_t _b) { return simd256_i8_cmpeq(_a, _b); } template<> BX_SIMD_FORCE_INLINE simd256_t simd_u8_satadd(simd256_t _a, simd256_t _b) { return simd256_u8_satadd(_a, _b); } @@ -2446,6 +2457,8 @@ namespace bx template<> BX_SIMD_FORCE_INLINE simd32_t simd_i32_mul(simd32_t _a, simd32_t _b) { return simd32_u32_mul(_a, _b); } + template<> + BX_SIMD_FORCE_INLINE simd32_t simd_i8_cmpeq(simd32_t _a, simd32_t _b) { return simd32_i8_cmpeq(_a, _b); } template<> BX_SIMD_FORCE_INLINE simd32_t simd_u8_satadd(simd32_t _a, simd32_t _b) { return simd32_u8_satadd(_a, _b); } @@ -2826,6 +2839,8 @@ namespace bx template<> BX_SIMD_FORCE_INLINE simd64_t simd_i32_mul(simd64_t _a, simd64_t _b) { return simd64_u32_mul(_a, _b); } + template<> + BX_SIMD_FORCE_INLINE simd64_t simd_i8_cmpeq(simd64_t _a, simd64_t _b) { return simd64_i8_cmpeq(_a, _b); } template<> BX_SIMD_FORCE_INLINE simd64_t simd_u8_satadd(simd64_t _a, simd64_t _b) { return simd64_u8_satadd(_a, _b); } diff --git a/include/bx/simd_t.h b/include/bx/simd_t.h index 89a43ce..dd75e9f 100644 --- a/include/bx/simd_t.h +++ b/include/bx/simd_t.h @@ -404,6 +404,18 @@ namespace bx template Ty simd_u8_satadd(Ty _a, Ty _b); + /// Per-lane i8 compare equal. Lanes set to all-ones if equal. + /// + /// @param[in] _a First operand. + /// @param[in] _b Second operand. + /// + /// @returns Per-lane comparison mask. + /// + /// @remark Widths: simd32, simd64, simd128, simd256. + /// + template + Ty simd_i8_cmpeq(Ty _a, Ty _b); + /// Per-lane unsigned 8-bit saturating subtract: `max(_a - _b, 0)`. /// /// @param[in] _a Minuend. diff --git a/src/bitarray.cpp b/src/bitarray.cpp index eb6cd20..20417ac 100644 --- a/src/bitarray.cpp +++ b/src/bitarray.cpp @@ -217,7 +217,7 @@ namespace bx return findLastWordRef(_ptr, _begin, _end); } - constexpr uint32_t kSimdByteMask = 32 == sizeof(SimdWideT) ? UINT32_MAX : (1u << sizeof(SimdWideT) ) - 1; + constexpr uint32_t kSimdByteMask = uint32_t( (UINT64_C(1) << sizeof(SimdWideT) ) - 1); template constexpr uint64_t kElementLowBits = 1 == sizeof(Ty) ? UINT64_C(0x0101010101010101) diff --git a/tests/simd_test.cpp b/tests/simd_test.cpp index fcf3458..81314b8 100644 --- a/tests/simd_test.cpp +++ b/tests/simd_test.cpp @@ -1275,6 +1275,13 @@ TEST_CASE("simd128_i32_cmpeq", "[simd]") check_u32("i32_cmpeq", simd128_i32_cmpeq(a, b), 0xffffffff, 0, 0xffffffff, 0); } +TEST_CASE("simd128_i8_cmpeq", "[simd]") +{ + const simd128_t a = simd128_ld(0x00ff7f01u, 0x80808080u, 0xfefefefeu, 0x10204080u); + const simd128_t b = simd128_ld(0x00fe7f80u, 0x80008080u, 0xfefeffffu, 0x10204081u); + check_u32("i8_cmpeq", simd128_i8_cmpeq(a, b), 0xff00ff00u, 0xff00ffffu, 0xffff0000u, 0xffffff00u); +} + TEST_CASE("simd128_i32_cmpgt", "[simd]") { const simd128_t a = simd128_ld(5u, 2u, 7u, 4u); @@ -2003,6 +2010,13 @@ TEST_CASE("simd32_i8_add_sub", "[simd]") REQUIRE(simd32_i8_sub(a, b).u32 == 0xf1f2f3f4); } +TEST_CASE("simd32_i8_cmpeq", "[simd]") +{ + REQUIRE(simd32_i8_cmpeq(make_u32(0x00ff7f01), make_u32(0x00fe7f80)).u32 == 0xff00ff00); + REQUIRE(simd32_i8_cmpeq(make_u32(0x80808080), make_u32(0x80808080)).u32 == 0xffffffff); + REQUIRE(simd32_i8_cmpeq(make_u32(0x01020304), make_u32(0x10203040)).u32 == 0); +} + TEST_CASE("simd32_u8_sat", "[simd]") { // satadd: 0xf0 + 0x20 = saturates to 0xff per byte