endianmacros: Replace borked PA_FLOAT32_SWAP() with PA_READ_FLOAT32RE() / PA_WRITE_FLOAT32RE()

building PA with -O0 leads to test failure in mix-test on i386

issue reported by Felipe, see
http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-August/021406.html

the problem is the value 0xbeffbd7f: when byte-swapped it becomes 0x7fbdffbe and according
to IEEE-754 represents a signalling NaN (starting with s111 1111 10, see http://en.wikipedia.org/wiki/NaN)

when this value is assigned to a floating point register, it becomes 0x7ffdffbe, representing
a quiet NaN (starting with s111 1111 11) -- a signalling NaN is turned into a quiet NaN!

so PA_FLOAT32_SWAP(PA_FLOAT32_SWAP(x)) != x for certain values, uhuh!

the following test code can be used; due to volatile, it will always demonstrate the issue;
without volatile, it depends on the optimization level (i386, 32-bit, gcc 4.9):

// snip

static inline float PA_FLOAT32_SWAP(float x) {
    union {
        float f;
        uint32_t u;
    } t;

    t.f = x;
    t.u = bswap_32(t.u);
    return t.f;
}

int main() {
  unsigned x = 0xbeffbd7f;
  volatile float f = PA_FLOAT32_SWAP(*(float *)&x);
  printf("%08x %08x %08x %f\n", 0xbeffbd7f, *(unsigned *)&f, bswap_32(*(unsigned *)&f), f);
}
// snip

the problem goes away with optimization when no temporary floating point registers are used

the proposed solution is to avoid passing swapped floating point data in a
float; this is done with new functions PA_READ_FLOAT32RE() and PA_WRITE_FLOAT32RE()
which use uint32_t to dereference a pointer and byte-swap the data, hence no temporary
float variable is used

also delete PA_FLOAT32_TO_LE()/_BE(), not used

Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net>
Reported-by: Felipe Sateler <fsateler@debian.org>
This commit is contained in:
Peter Meerwald 2014-09-02 23:53:09 +02:00
parent 5d7b5e509c
commit 293a1739e2
7 changed files with 32 additions and 40 deletions

View file

@ -157,8 +157,7 @@ void pa_sconv_s16le_to_float32re(unsigned n, const int16_t *a, float *b) {
for (; n > 0; n--) {
int16_t s = *(a++);
float k = INT16_FROM(s) * (1.0f / (1 << 15));
k = PA_FLOAT32_SWAP(k);
*(b++) = k;
PA_WRITE_FLOAT32RE(b++, k);
}
}
@ -169,8 +168,7 @@ void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b) {
for (; n > 0; n--) {
int32_t s = *(a++);
float k = INT32_FROM(s) * (1.0f / (1U << 31));
k = PA_FLOAT32_SWAP(k);
*(b++) = k;
PA_WRITE_FLOAT32RE(b++, k);
}
}
@ -180,8 +178,7 @@ void pa_sconv_s16le_from_float32re(unsigned n, const float *a, int16_t *b) {
for (; n > 0; n--) {
int16_t s;
float v = *(a++);
v = PA_FLOAT32_SWAP(v) * (1 << 15);
float v = PA_READ_FLOAT32RE(a++) * (1 << 15);
s = (int16_t) PA_CLAMP_UNLIKELY(lrintf(v), -0x8000, 0x7FFF);
*(b++) = INT16_TO(s);
}
@ -193,8 +190,7 @@ void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b) {
for (; n > 0; n--) {
int32_t s;
float v = *(a++);
v = PA_FLOAT32_SWAP(v) * (1U << 31);
float v = PA_READ_FLOAT32RE(a++) * (1U << 31);
s = (int32_t) PA_CLAMP_UNLIKELY(llrintf(v), -0x80000000LL, 0x7FFFFFFFLL);
*(b++) = INT32_TO(s);
}
@ -325,7 +321,7 @@ void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b) {
for (; n > 0; n--) {
int32_t s = READ24(a) << 8;
float k = s * (1.0f / (1U << 31));
*b = PA_FLOAT32_SWAP(k);
PA_WRITE_FLOAT32RE(b, k);
a += 3;
b++;
}
@ -337,8 +333,7 @@ void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b) {
for (; n > 0; n--) {
int32_t s;
float v = *a;
v = PA_FLOAT32_SWAP(v) * (1U << 31);
float v = PA_READ_FLOAT32RE(a) * (1U << 31);
s = (int32_t) PA_CLAMP_UNLIKELY(llrint(v), -0x80000000LL, 0x7FFFFFFFLL);
WRITE24(b, ((uint32_t) s) >> 8);
a++;
@ -411,7 +406,7 @@ void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) {
for (; n > 0; n--) {
int32_t s = (int32_t) (UINT32_FROM(*a) << 8);
float k = s * (1.0f / (1U << 31));
*b = PA_FLOAT32_SWAP(k);
PA_WRITE_FLOAT32RE(b, k);
a++;
b++;
}
@ -437,8 +432,7 @@ void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b) {
for (; n > 0; n--) {
int32_t s;
float v = *a;
v = PA_FLOAT32_SWAP(v) * (1U << 31);
float v = PA_READ_FLOAT32RE(a) * (1U << 31);
s = (int32_t) PA_CLAMP_UNLIKELY(llrint(v), -0x80000000LL, 0x7FFFFFFFLL);
*b = UINT32_TO(((uint32_t) s) >> 8);
a++;