feat: add SSE2 support and fix SSE4.1 paths for PADDSW and PSUBSW instructions (#141)

* feat: add sses2 support for PADDSW and PSUBSW instructions

* Fixed SSE4.1 PADDSW/PSUBSW implementations
This commit is contained in:
Jeffrey Shulkin
2026-07-03 23:02:00 -04:00
committed by GitHub
parent db3d44fbde
commit 3a2e0d69fd
2 changed files with 56 additions and 7 deletions
+2 -7
View File
@@ -2057,15 +2057,10 @@ namespace ps2recomp
return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSUBB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt);
case MMI0_PCGTB:
return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PCGTB(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt);
// thouse 2 require SSE4.1 now TODO implement on SSE2
case MMI0_PADDSW:
return fmt::format("SET_GPR_VEC(ctx, {}, _mm_min_epi32(_mm_max_epi32(_mm_add_epi32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})), "
"_mm_set1_epi32(INT32_MIN)), _mm_set1_epi32(INT32_MAX)));",
rd, rs, rt);
return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PADDSW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt);
case MMI0_PSUBSW:
return fmt::format("SET_GPR_VEC(ctx, {}, _mm_min_epi32(_mm_max_epi32(_mm_sub_epi32(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})), "
"_mm_set1_epi32(INT32_MIN)), _mm_set1_epi32(INT32_MAX)));",
rd, rs, rt);
return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PSUBSW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt);
case MMI0_PEXTLW:
return fmt::format("SET_GPR_VEC(ctx, {}, PS2_PEXTLW(GPR_VEC(ctx, {}), GPR_VEC(ctx, {})));", rd, rs, rt);
case MMI0_PPACW:
+54
View File
@@ -426,6 +426,60 @@ static inline void Ps2FastWrite128(uint8_t *rdram, uint32_t addr, __m128i value)
#define PS2_PCGTH(a, b) _mm_cmpgt_epi16((__m128i)(a), (__m128i)(b))
#define PS2_PCGTB(a, b) _mm_cmpgt_epi8((__m128i)(a), (__m128i)(b))
// Packed Add with Signed Saturation Word (PADDSW)
inline __m128i ps2_paddsw(__m128i a, __m128i b)
{
__m128i sum = _mm_add_epi32(a, b);
// Check for over/underflow. Clamp to either INT32_MIN/INT32_MAX.
__m128i overflow = _mm_and_si128(_mm_xor_si128(a, sum),
_mm_xor_si128(b, sum));
// Extract input sign.
overflow = _mm_srai_epi32(overflow, 31);
__m128i input_sign = _mm_srai_epi32(a, 31);
// Select saturation value based on overflow sign.
#if defined(__SSE4_1__)
__m128i sat = _mm_blendv_epi8(
_mm_set1_epi32(INT32_MAX),
_mm_set1_epi32(INT32_MIN),
input_sign);
return _mm_blendv_epi8(sum, sat, overflow);
#else
__m128i sat = _mm_or_si128(_mm_and_si128(input_sign, _mm_set1_epi32(INT32_MIN)),
_mm_andnot_si128(input_sign, _mm_set1_epi32(INT32_MAX)));
return _mm_or_si128(_mm_and_si128(overflow, sat),
_mm_andnot_si128(overflow, sum));
#endif
}
#define PS2_PADDSW(a, b) ps2_paddsw((__m128i)(a), (__m128i)(b))
// Packed Subtract with Signed Saturation Word (PSUBSW)
inline __m128i ps2_psubsw(__m128i a, __m128i b)
{
__m128i diff = _mm_sub_epi32(a, b);
// Check for over/underflow. Clamp to either INT32_MIN/INT32_MAX.
__m128i overflow = _mm_and_si128(_mm_xor_si128(a, b),
_mm_xor_si128(a, diff));
// Extract input sign.
overflow = _mm_srai_epi32(overflow, 31);
__m128i input_sign = _mm_srai_epi32(a, 31);
// Select saturation value based on overflow sign.
#if defined(__SSE4_1__)
__m128i sat = _mm_blendv_epi8(
_mm_set1_epi32(INT32_MAX),
_mm_set1_epi32(INT32_MIN),
input_sign);
return _mm_blendv_epi8(diff, sat, overflow);
#else
__m128i sat = _mm_or_si128(_mm_and_si128(input_sign, _mm_set1_epi32(INT32_MIN)),
_mm_andnot_si128(input_sign, _mm_set1_epi32(INT32_MAX)));
return _mm_or_si128(_mm_and_si128(overflow, sat),
_mm_andnot_si128(overflow, diff));
#endif
}
#define PS2_PSUBSW(a, b) ps2_psubsw((__m128i)(a), (__m128i)(b))
// Packed Compare Equal (PCEQ)
#define PS2_PCEQW(a, b) _mm_cmpeq_epi32((__m128i)(a), (__m128i)(b))
#define PS2_PCEQH(a, b) _mm_cmpeq_epi16((__m128i)(a), (__m128i)(b))