From 072e76019740581a32992be94c1b9ac966469899 Mon Sep 17 00:00:00 2001
From: Marcin Serwin <[EMAIL REDACTED]>
Date: Fri, 21 Mar 2025 17:01:01 +0100
Subject: [PATCH] audio: hold floating point exceptions when using neon
(cherry picked from commit 4fcef9074b42e02e2a9fb7104810c03ac78f8806)
---
src/audio/SDL_audiotypecvt.c | 57 ++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/src/audio/SDL_audiotypecvt.c b/src/audio/SDL_audiotypecvt.c
index d80a831f936d7..a27575f23947c 100644
--- a/src/audio/SDL_audiotypecvt.c
+++ b/src/audio/SDL_audiotypecvt.c
@@ -22,6 +22,10 @@
#include "SDL_sysaudio.h"
+#ifdef SDL_NEON_INTRINSICS
+#include <fenv.h>
+#endif
+
#define DIVBY2147483648 0.0000000004656612873077392578125f // 0x1p-31f
// start fallback scalar converters
@@ -527,9 +531,27 @@ static void SDL_TARGETING("ssse3") SDL_Convert_Swap32_SSSE3(Uint32* dst, const U
#endif
#ifdef SDL_NEON_INTRINSICS
+
+// C99 requires that all code modifying floating point environment should
+// be guarded by the STDC FENV_ACCESS pragma; otherwise, it's undefined
+// behavior. However, the compiler support for this pragma is bad.
+#if defined(__clang__)
+#if __clang_major__ >= 12
+#pragma STDC FENV_ACCESS ON
+#endif
+#elif defined(_MSC_VER)
+#pragma fenv_access (on)
+#elif defined(__GNUC__)
+// GCC does not support the pragma at all
+#else
+#pragma STDC FENV_ACCESS ON
+#endif
+
static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
CONVERT_16_REV({
vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vdup_n_s32(src[i]), 7), 0);
@@ -549,11 +571,14 @@ static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_sam
vst1q_f32(&dst[i + 8], floats2);
vst1q_f32(&dst[i + 12], floats3);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_U8_to_F32_NEON(float *dst, const Uint8 *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
uint8x16_t flipper = vdupq_n_u8(0x80);
@@ -575,11 +600,14 @@ static void SDL_Convert_U8_to_F32_NEON(float *dst, const Uint8 *src, int num_sam
vst1q_f32(&dst[i + 8], floats2);
vst1q_f32(&dst[i + 12], floats3);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_S16_to_F32_NEON(float *dst, const Sint16 *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
CONVERT_16_REV({
vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vdup_n_s32(src[i]), 15), 0);
@@ -597,11 +625,14 @@ static void SDL_Convert_S16_to_F32_NEON(float *dst, const Sint16 *src, int num_s
vst1q_f32(&dst[i + 8], floats2);
vst1q_f32(&dst[i + 12], floats3);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_S32_to_F32_NEON(float *dst, const Sint32 *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
CONVERT_16_FWD({
vst1_lane_f32(&dst[i], vcvt_n_f32_s32(vld1_dup_s32(&src[i]), 31), 0);
@@ -621,11 +652,14 @@ static void SDL_Convert_S32_to_F32_NEON(float *dst, const Sint32 *src, int num_s
vst1q_f32(&dst[i + 8], floats2);
vst1q_f32(&dst[i + 12], floats3);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_F32_to_S8_NEON(Sint8 *dst, const float *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
CONVERT_16_FWD({
vst1_lane_s8(&dst[i], vreinterpret_s8_s32(vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31)), 3);
@@ -647,11 +681,14 @@ static void SDL_Convert_F32_to_S8_NEON(Sint8 *dst, const float *src, int num_sam
vst1q_s8(&dst[i], bytes);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_F32_to_U8_NEON(Uint8 *dst, const float *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
uint8x16_t flipper = vdupq_n_u8(0x80);
@@ -679,11 +716,14 @@ static void SDL_Convert_F32_to_U8_NEON(Uint8 *dst, const float *src, int num_sam
vst1q_u8(&dst[i], bytes);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_F32_to_S16_NEON(Sint16 *dst, const float *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
CONVERT_16_FWD({
vst1_lane_s16(&dst[i], vreinterpret_s16_s32(vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31)), 1);
@@ -704,11 +744,14 @@ static void SDL_Convert_F32_to_S16_NEON(Sint16 *dst, const float *src, int num_s
vst1q_s16(&dst[i], shorts0);
vst1q_s16(&dst[i + 8], shorts1);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_F32_to_S32_NEON(Sint32 *dst, const float *src, int num_samples)
{
LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using NEON)");
+ fenv_t fenv;
+ feholdexcept(&fenv);
CONVERT_16_FWD({
vst1_lane_s32(&dst[i], vcvt_n_s32_f32(vld1_dup_f32(&src[i]), 31), 0);
@@ -728,6 +771,7 @@ static void SDL_Convert_F32_to_S32_NEON(Sint32 *dst, const float *src, int num_s
vst1q_s32(&dst[i + 8], ints2);
vst1q_s32(&dst[i + 12], ints3);
})
+ fesetenv(&fenv);
}
static void SDL_Convert_Swap16_NEON(Uint16* dst, const Uint16* src, int num_samples)
@@ -767,6 +811,19 @@ static void SDL_Convert_Swap32_NEON(Uint32* dst, const Uint32* src, int num_samp
vst1q_u8((Uint8*)&dst[i + 12], ints3);
})
}
+
+#if defined(__clang__)
+#if __clang_major__ >= 12
+#pragma STDC FENV_ACCESS DEFAULT
+#endif
+#elif defined(_MSC_VER)
+#pragma fenv_access (off)
+#elif defined(__GNUC__)
+//
+#else
+#pragma STDC FENV_ACCESS DEFAULT
+#endif
+
#endif
#undef CONVERT_16_FWD