When building for 32-bit x86 but with SSE2 floating-point enabled: -m32 -msse2 -mfpmath=sse the conversion from unsigned int 0 to double produces the result of -0.0 when floating-point rounding mode is set to FE_DOWNWARD. I used -frounding-math and #pragma STDC FENV_ACCESS ON. This bug is not present on x87 nor x86_64 builds. The bug seems to be present at least since GCC 5. #include <fenv.h> #pragma STDC FENV_ACCESS ON __attribute__((noinline)) double u32_to_f64(unsigned x) { return static_cast<double>(x); } int main() { fesetround(FE_DOWNWARD); double d = u32_to_f64(0); return __builtin_signbit(d) != 0; // signbit should be 0 } The assembly: u32_to_f64(unsigned int): sub esp, 12 pxor xmm0, xmm0 mov eax, DWORD PTR [esp+16] add eax, -2147483648 cvtsi2sd xmm0, eax addsd xmm0, QWORD PTR .LC0 movsd QWORD PTR [esp], xmm0 fld QWORD PTR [esp] add esp, 12 ret main: lea ecx, [esp+4] and esp, -16 push DWORD PTR [ecx-4] push ebp mov ebp, esp push ecx sub esp, 32 push 1024 call fesetround mov DWORD PTR [esp], 0 call u32_to_f64(unsigned int) mov ecx, DWORD PTR [ebp-4] add esp, 16 fstp QWORD PTR [ebp-16] movsd xmm0, QWORD PTR [ebp-16] leave lea esp, [ecx-4] movmskpd eax, xmm0 and eax, 1 ret .LC0: .long 0 .long 1105199104 https://godbolt.org/z/rrMWY9jsG
Confirmed.
diff --git a/gcc/config/i386/i386-expand.c b/gcc/config/i386/i386-expand.c index dda08ff67f2..5a7a00c13bd 100644 --- a/gcc/config/i386/i386-expand.c +++ b/gcc/config/i386/i386-expand.c @@ -1550,6 +1550,8 @@ ix86_expand_convert_uns_sixf_sse (rtx, rtx) gcc_unreachable (); } +static rtx ix86_expand_sse_fabs (rtx op0, rtx *smask); + /* Convert an unsigned SImode value into a DFmode. Only currently used for SSE, but applicable anywhere. */ @@ -1569,6 +1571,11 @@ ix86_expand_convert_uns_sidf_sse (rtx target, rtx input) x = const_double_from_real_value (TWO31r, DFmode); x = expand_simple_binop (DFmode, PLUS, fp, x, target, 0, OPTAB_DIRECT); + + /* Remove the sign with FE_DOWNWARD, where x - x = -0.0. */ + if (HONOR_SIGNED_ZEROS (DFmode) && flag_rounding_math) + x = ix86_expand_sse_fabs (x, NULL); + if (x != target) emit_move_insn (target, x); }
The master branch has been updated by Uros Bizjak <uros@gcc.gnu.org>: https://gcc.gnu.org/g:0cda606d08d6196b76524c7b6ad51d87fed0d54b commit r12-61-g0cda606d08d6196b76524c7b6ad51d87fed0d54b Author: Uros Bizjak <ubizjak@gmail.com> Date: Thu Apr 22 16:30:38 2021 +0200 i386: Fix unsigned int -> double conversion on i386 w/ -mfpmath=sse [PR100119] 2021-04-22 Uroš Bizjak <ubizjak@gmail.com> gcc/ PR target/100119 * config/i386/i386-expand.c (ix86_expand_convert_uns_sidf_sse): Remove the sign with FE_DOWNWARD, where x - x = -0.0. gcc/testsuite/ PR target/100119 * gcc.target/i386/pr100119.c: New test.
So fixed for GCC 12?
(In reply to Richard Biener from comment #4) > So fixed for GCC 12? Yes.