Commit 6f7d8279 authored by Tamar Christina's avatar Tamar Christina Committed by Ben Gamari

Reset FPU precision back to MSVCRT defaults

Mingw-w64 does a stupid thing. They set the FPU precision to extended
mode by default.  The reasoning is that it's for compatibility with GNU
Linux ported libraries. However the problem is this is incompatible with
the standard Windows double precision mode.  In fact, if we create a new
OS thread then Windows will reset the FPU to double precision mode.  So
we end up with a weird state where the main thread by default has a
different precision than any child threads.

Test Plan: ./validate new test T7289

Reviewers: simonmar, austin, bgamari, erikd

Reviewed By: simonmar

Subscribers: thomie, #ghc_windows_task_force

Differential Revision: https://phabricator.haskell.org/D2819

GHC Trac Issues: #7289
parent 0c3341b2
......@@ -46,7 +46,9 @@
#include "win32/AsyncIO.h"
#endif
#if !defined(mingw32_HOST_OS)
#if defined(mingw32_HOST_OS)
#include <fenv.h>
#else
#include "posix/TTY.h"
#endif
......@@ -69,10 +71,18 @@ static void flushStdHandles(void);
#define X86_INIT_FPU 0
#if X86_INIT_FPU
static void
x86_init_fpu ( void )
{
#if defined(mingw32_HOST_OS) && !X86_INIT_FPU
/* Mingw-w64 does a stupid thing. They set the FPU precision to extended mode by default.
The reasoning is that it's for compatibility with GNU Linux ported libraries. However the
problem is this is incompatible with the standard Windows double precision mode. In fact,
if we create a new OS thread then Windows will reset the FPU to double precision mode.
So we end up with a weird state where the main thread by default has a different precision
than any child threads. */
fesetenv(FE_PC53_ENV);
#elif X86_INIT_FPU
__volatile unsigned short int fpu_cw;
// Grab the control word
......@@ -87,8 +97,26 @@ x86_init_fpu ( void )
// Store the new control word back
__asm __volatile ("fldcw %0" : : "m" (fpu_cw));
#else
return;
#endif
}
#if defined(mingw32_HOST_OS)
/* And now we have to override the build in ones in Mingw-W64's CRT. */
void _fpreset(void)
{
x86_init_fpu();
}
#ifdef __GNUC__
void __attribute__((alias("_fpreset"))) fpreset(void);
#else
void fpreset(void) {
_fpreset();
}
#endif
#endif
/* -----------------------------------------------------------------------------
Starting up the RTS
......@@ -244,9 +272,7 @@ hs_init_ghc(int *argc, char **argv[], RtsConfig rts_config)
startupAsyncIO();
#endif
#if X86_INIT_FPU
x86_init_fpu();
#endif
startupHpc();
......
TOP=../../..
include $(TOP)/mk/boilerplate.mk
include $(TOP)/mk/test.mk
module Main where
import Control.Concurrent
foreign import ccall "showControlBits" checkfpu :: IO ()
main
= do checkfpu
forkOS checkfpu
test('T7289', [ extra_clean(['fp.o', 'testfp.o', 'testfp.hi'])
, extra_files(['fp.c'])
, unless(opsys('mingw32'), skip)
, only_ways(['threaded1'])
],
compile_and_run, ['fp.c'])
#include <stdio.h>
#include <xmmintrin.h>
#include <float.h>
static unsigned int
getFPUStateX86 (void)
{
unsigned int control = 0;
#if defined(_MSC_VER)
control = _controlfp(0, 0);
#else
__asm__ __volatile__("fnstcw %0" : "=m" (control));
#endif
return control;
}
static unsigned int
getSSEStateX86 (void)
{
return _mm_getcsr();
}
extern void showControlBits (void)
{
printf("FPU: 0x%04x\n", getFPUStateX86());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment