Bug 51007 - [4.7 Regression] Quadmath I/O doesn't work on MinGW
Summary: [4.7 Regression] Quadmath I/O doesn't work on MinGW
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 4.7.0
: P3 normal
Target Milestone: 4.7.0
Assignee: Not yet assigned to anyone
URL:
Keywords: patch
Depends on:
Blocks:
 
Reported: 2011-11-07 10:57 UTC by Francois-Xavier Coudert
Modified: 2019-06-16 21:14 UTC (History)
3 users (show)

See Also:
Host:
Target: i586-pc-mingw32,x86_64-w64-mingw32
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-11-07 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Francois-Xavier Coudert 2011-11-07 10:57:00 UTC
The following test program works fine with current trunk on i686 and x86_64-linux:


#include <quadmath.h>
#include <stdio.h>

int main (void)
{
  char buf[100];
  quadmath_snprintf (buf, sizeof buf, "%.60Qg", (__float128) 3.14);
  puts (buf);
}


It outputs "3.140000000000000124344978758017532527446746826171875". When compiled and run on MinGW, it gives an incorrect output (on my test system, "1.9163987915738935076000483121420225908698150840344985584492e-4932"). I confirmed with both a native MinGW build (i586-pc-mingw32) and a cross-compiler for i586-pc-mingw32, running on x86_64-apple-darwin11, and running the result under either Wine or a real Windows.

The cross-compiler is configured as such:


../../gcc/trunk/configure --target=i586-pc-mingw32 --disable-werror --enable-languages=c,fortran
Comment 1 Francois-Xavier Coudert 2011-11-07 11:03:32 UTC
I don't know if it's related, or two different bugs, but hexadecimal printing has the correct mantissa with a wrong exponent. See below the same testcase compile on my mac (native compiler) and cross-compiled to mingw:

$ cat v.c 
#include <quadmath.h>
#include <stdio.h>

int main (void)
{
  char buf[100];
  quadmath_snprintf (buf, sizeof buf, "%.60Qa", (__float128) 3.14);
  puts (buf);
}
$ gcc-4.6 v.c -lquadmath && ./a.out 
0x1.91eb851eb851f00000000000000000000000000000000000000000000000p+1
$ ./cross/bin/i586-pc-mingw32-gcc v.c -static -lquadmath
$ /opt/wine/bin/wine ./a.exe 
0x1.91eb851eb851f00000000000000000000000000000000000000000000000p-5318
Comment 2 Francois-Xavier Coudert 2011-11-07 11:29:39 UTC
The bug is not in the I/O routine, it can be reproduce by this simple self-contained testcase (which doesn't need libquadmath).

#include <stdint.h>

typedef union
{
  __float128 value;

  struct
  {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    unsigned negative:1;
    unsigned exponent:15;
    uint64_t mant_high:48;
    uint64_t mant_low:64;
#else
    uint64_t mant_low:64;
    uint64_t mant_high:48;
    unsigned exponent:15;
    unsigned negative:1;
#endif
  } ieee;
} ieee854_float128;

int main (void)
{
  ieee854_float128 d;
  d.value = (__float128) 3.14;
  __builtin_printf ("%u\n", (unsigned) d.ieee.exponent);
}

On darwin, this program consistently outputs 16384. When cross-compiled to mingw, and run on Windows XP, it consistently outputs 6448. When the same executable is run under Wine on darwin, it outputs inconsistent, wrong values (the last few invocations here show: 4755, 12947, 21139).
Comment 3 Francois-Xavier Coudert 2011-11-07 11:39:12 UTC
Going further: I tried to compare the trees generated by the simple function below:


#include <stdint.h>

typedef union
{
  __float128 value;

  struct
  {
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
    unsigned negative:1;
    unsigned exponent:15;
    uint64_t mant_high:48;
    uint64_t mant_low:64;
#else
    uint64_t mant_low:64;
    uint64_t mant_high:48;
    unsigned exponent:15;
    unsigned negative:1;
#endif
  } ieee;
} ieee854_float128;

unsigned foo (__float128 x)
{
  ieee854_float128 d;
  d.value = x;
  return d.ieee.exponent;
}


AFAICT, both the original and the optimized tree for darwin and mingw are identical. The assembly code generated for a common arch (-march=core2 -O0) differs:

***** on mingw *****
_foo:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $40, %esp
        movdqa  8(%ebp), %xmm0
        movdqa  %xmm0, -40(%ebp)
        movzwl  -24(%ebp), %eax
        andw    $32767, %ax
        movzwl  %ax, %eax
        leave
        ret

***** on darwin *****
_foo:
	pushq	%rbp
	movq	%rsp, %rbp
	movdqa	%xmm0, -32(%rbp)
	movdqa	-32(%rbp), %xmm0
	movdqa	%xmm0, -16(%rbp)
	movzwl	-2(%rbp), %eax
	andw	$32767, %ax
	movzwl	%ax, %eax
	popq	%rbp
	ret


Now, my assembly skills are pretty close to nil, so I can't figure out if the differences are meaningful, and what they mean. I only hope that I got it close enough that someone can, from here, understand where this code generation come from and how to fix it.
Comment 4 Kai Tietz 2011-11-07 12:00:49 UTC
The issue is caused by bitfield layout.  For mingw targets the -mms-bitfields option is for 4.7 active by default.

So  the mixture of different sized types in union is leading to this behavior.
You can see that result becomes ok, if you are specifying to union/struct the attribute gcc_struct.

Cheers,
Kai
Comment 5 Kai Tietz 2011-11-07 12:19:09 UTC
Suggested patch for this issue

ChangeLog

        * quadmath-imp.h (ieee854_float128): Adjust
        for ms-bitfield layout.

Index: quadmath-imp.h
===================================================================
--- quadmath-imp.h      (revision 180840)
+++ quadmath-imp.h      (working copy)
@@ -48,6 +48,11 @@
   __float128 value;

   struct
+#ifdef __MINGW32__
+  /* On mingw targets ms-bitfields option is active by default.
+     Therefore enforce gnu-bitfield style.  */
+  __attribute__ ((gcc_struct))
+#endif
   {
 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
     unsigned negative:1;
@@ -89,6 +94,10 @@
   } words32;

   struct
+#ifdef __MINGW32__
+  /* Make sure we are using gnu-style bitfield handling.  */
+  __attribute__ ((gcc_struct))
+#endif
   {
 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
     unsigned negative:1;
Comment 6 Francois-Xavier Coudert 2011-11-07 12:42:26 UTC
(In reply to comment #4)
> So  the mixture of different sized types in union is leading to this behavior.
> You can see that result becomes ok, if you are specifying to union/struct the
> attribute gcc_struct.

Indeed, I confirm that your patch fixes the issue (and the original Fortran testcase reported on comp.lang.fortran now works fine). Both Tobias and Jakub (Cc'ed) are libquadmath maintainers and can approve it.
Comment 7 Tobias Burnus 2011-11-07 15:29:52 UTC
(In reply to comment #5)
> ChangeLog
> 
>         * quadmath-imp.h (ieee854_float128): Adjust
>         for ms-bitfield layout.
> 
> +#ifdef __MINGW32__
> +  /* On mingw targets ms-bitfields option is active by default.
> +     Therefore enforce gnu-bitfield style.  */
> +  __attribute__ ((gcc_struct))
> +#endif

The patch looks fine to me - except that I would put an "the" before "ms-bitfields option".
Comment 8 Kai Tietz 2011-11-07 22:04:00 UTC
Author: ktietz
Date: Mon Nov  7 22:03:51 2011
New Revision: 181125

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=181125
Log:
        PR target/51007
        * quadmath-imp.h (ieee854_float128): Adjust
        for ms-bitfield layout.


Modified:
    trunk/libquadmath/ChangeLog
    trunk/libquadmath/quadmath-imp.h
Comment 9 Kai Tietz 2011-11-07 22:05:31 UTC
Applied with suggested "the" before ms-bitfield option.
Fixed.
Comment 10 Benjamin Kosnik 2012-05-31 18:51:34 UTC
Author: bkoz
Date: Thu May 31 18:51:27 2012
New Revision: 188076

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=188076
Log:
2012-05-31  Benjamin Kosnik  <bkoz@redhat.com>

        PR libstdc++/51007
        * configure.ac: Allow gnu, gnu* variants for --enable-symvers argument.
        * configure: Regenerated.

Modified:
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/configure
    trunk/libgfortran/configure.ac
    trunk/libquadmath/ChangeLog
    trunk/libquadmath/configure
    trunk/libquadmath/configure.ac
    trunk/libssp/ChangeLog
    trunk/libssp/configure
    trunk/libssp/configure.ac