msvcrt: Don't reset MXCSR status in __control87_2 if control information is not changed.

Signed-off-by: Myah Caron <qsniyg@protonmail.com>
Signed-off-by: Piotr Caban <piotr@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Myah Caron 2020-07-07 16:07:39 +02:00 committed by Alexandre Julliard
parent 68204a86a2
commit f2a804b9ea
3 changed files with 95 additions and 40 deletions

View File

@ -22,6 +22,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <float.h>
#include <math.h>
/* the tests intentionally pass invalid pointers and need an exception handler */
#define WINE_NO_INLINE_STRING
@ -1829,6 +1830,28 @@ static inline unsigned long get_fpu_cw(void)
#endif
}
static inline void fpu_invalid_operation(void)
{
double d;
#if defined(__i386__)
unsigned int sse;
#ifdef _MSC_VER
__asm { stmxcsr [sse] }
sse |= 1; /* invalid operation flag */
__asm { ldmxcsr [sse] }
#else
__asm__ volatile ("stmxcsr %0" : "=m" (sse));
sse |= 1;
__asm__ volatile ("ldmxcsr %0" : : "m" (sse));
#endif
#endif
d = acos(2.0);
ok(_isnan(d), "d = %lf\n", d);
ok(_statusfp() == _SW_INVALID, "_statusfp() = %x\n", _statusfp());
}
static DWORD WINAPI fpu_thread(void *param)
{
struct fpu_thread_ctx *ctx = param;
@ -1870,7 +1893,7 @@ static void test_thread_fpu_cw(void)
{
static const struct {
unsigned int cw; unsigned long fpu_cw; unsigned long fpu_cw_broken;
} expected_cw[6] =
} expected_cw[8] =
{
#ifdef __i386__
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f80 ) },
@ -1878,20 +1901,26 @@ static void test_thread_fpu_cw(void)
{ _EM_INEXACT | _RC_CHOP | _PC_24, MAKELONG( 0xc60, 0x7000 ), MAKELONG( 0xc60, 0x1f80 ) },
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f80 ) },
{ _EM_INEXACT | _RC_CHOP | _PC_24, MAKELONG( 0xc60, 0x7000 ), MAKELONG( 0xc60, 0x1f80 ) },
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f80 ) }
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f80 ) },
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f81 ) },
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f81 ) }
#elif defined(__x86_64__)
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f80 ) },
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f80 ) },
{ _EM_INEXACT | _RC_CHOP | _PC_64, MAKELONG( 0x27f, 0x7000 ) },
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f80 ) },
{ _EM_INEXACT | _RC_CHOP | _PC_64, MAKELONG( 0x27f, 0x7000 ) },
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f80 ) }
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f80 ) },
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f81 ) },
{ _MCW_EM | _PC_64, MAKELONG( 0x27f, 0x1f81 ) }
#elif defined(__aarch64__)
{ _MCW_EM | _PC_64, 0 },
{ _MCW_EM | _PC_64, 0 },
{ _EM_INEXACT | _RC_CHOP | _PC_64, 0xc08f00 },
{ _MCW_EM | _PC_64, 0 },
{ _EM_INEXACT | _RC_CHOP | _PC_64, 0xc08f00 },
{ _MCW_EM | _PC_64, 0 },
{ _MCW_EM | _PC_64, 0 },
{ _MCW_EM | _PC_64, 0 }
#else
{ 0xdeadbeef, 0xdeadbeef }
@ -1933,6 +1962,18 @@ static void test_thread_fpu_cw(void)
fpu_cw = get_fpu_cw();
ok(cw == expected_cw[5].cw, "expected %#x got %#x\n", expected_cw[5].cw, cw);
ok(fpu_cw == expected_cw[5].fpu_cw, "expected %#lx got %#lx\n", expected_cw[5].fpu_cw, fpu_cw);
fpu_invalid_operation();
cw = _control87( 0, 0 );
fpu_cw = get_fpu_cw();
ok(cw == expected_cw[6].cw, "expected %#x got %#x\n", expected_cw[6].cw, cw);
ok(fpu_cw == expected_cw[6].fpu_cw, "expected %#lx got %#lx\n", expected_cw[6].fpu_cw, fpu_cw);
cw = _control87( initial_cw, _MCW_EM | _MCW_RC | _MCW_PC );
fpu_cw = get_fpu_cw();
ok(cw == expected_cw[7].cw, "expected %#x got %#x\n", expected_cw[6].cw, cw);
ok(fpu_cw == expected_cw[7].fpu_cw, "expected %#lx got %#lx\n", expected_cw[6].fpu_cw, fpu_cw);
_clearfp();
}
static const char manifest_dep[] =

View File

@ -1107,6 +1107,7 @@ int CDECL __control87_2( unsigned int newval, unsigned int mask,
#ifdef __GNUC__
unsigned long fpword;
unsigned int flags;
unsigned int old_flags;
if (x86_cw)
{
@ -1196,29 +1197,34 @@ int CDECL __control87_2( unsigned int newval, unsigned int mask,
TRACE( "sse2 flags=%08x newval=%08x mask=%08x\n", flags, newval, mask );
if (mask)
{
old_flags = flags;
mask &= MSVCRT__MCW_EM | MSVCRT__MCW_RC | MSVCRT__MCW_DN;
flags = (flags & ~mask) | (newval & mask);
/* Convert (masked) value back to fp word */
fpword = 0;
if (flags & MSVCRT__EM_INVALID) fpword |= 0x80;
if (flags & MSVCRT__EM_DENORMAL) fpword |= 0x100;
if (flags & MSVCRT__EM_ZERODIVIDE) fpword |= 0x200;
if (flags & MSVCRT__EM_OVERFLOW) fpword |= 0x400;
if (flags & MSVCRT__EM_UNDERFLOW) fpword |= 0x800;
if (flags & MSVCRT__EM_INEXACT) fpword |= 0x1000;
switch (flags & MSVCRT__MCW_RC)
if (flags != old_flags)
{
case MSVCRT__RC_UP|MSVCRT__RC_DOWN: fpword |= 0x6000; break;
case MSVCRT__RC_UP: fpword |= 0x4000; break;
case MSVCRT__RC_DOWN: fpword |= 0x2000; break;
/* Convert (masked) value back to fp word */
fpword = 0;
if (flags & MSVCRT__EM_INVALID) fpword |= 0x80;
if (flags & MSVCRT__EM_DENORMAL) fpword |= 0x100;
if (flags & MSVCRT__EM_ZERODIVIDE) fpword |= 0x200;
if (flags & MSVCRT__EM_OVERFLOW) fpword |= 0x400;
if (flags & MSVCRT__EM_UNDERFLOW) fpword |= 0x800;
if (flags & MSVCRT__EM_INEXACT) fpword |= 0x1000;
switch (flags & MSVCRT__MCW_RC)
{
case MSVCRT__RC_UP|MSVCRT__RC_DOWN: fpword |= 0x6000; break;
case MSVCRT__RC_UP: fpword |= 0x4000; break;
case MSVCRT__RC_DOWN: fpword |= 0x2000; break;
}
switch (flags & MSVCRT__MCW_DN)
{
case MSVCRT__DN_FLUSH_OPERANDS_SAVE_RESULTS: fpword |= 0x0040; break;
case MSVCRT__DN_SAVE_OPERANDS_FLUSH_RESULTS: fpword |= 0x8000; break;
case MSVCRT__DN_FLUSH: fpword |= 0x8040; break;
}
__asm__ __volatile__( "ldmxcsr %0" : : "m" (fpword) );
}
switch (flags & MSVCRT__MCW_DN)
{
case MSVCRT__DN_FLUSH_OPERANDS_SAVE_RESULTS: fpword |= 0x0040; break;
case MSVCRT__DN_SAVE_OPERANDS_FLUSH_RESULTS: fpword |= 0x8000; break;
case MSVCRT__DN_FLUSH: fpword |= 0x8040; break;
}
__asm__ __volatile__( "ldmxcsr %0" : : "m" (fpword) );
}
*sse2_cw = flags;
}
@ -1246,6 +1252,7 @@ unsigned int CDECL _control87(unsigned int newval, unsigned int mask)
if ((flags ^ sse2_cw) & (MSVCRT__MCW_EM | MSVCRT__MCW_RC)) flags |= MSVCRT__EM_AMBIGUOUS;
#elif defined(__x86_64__)
unsigned long fpword;
unsigned int old_flags;
__asm__ __volatile__( "stmxcsr %0" : "=m" (fpword) );
if (fpword & 0x80) flags |= MSVCRT__EM_INVALID;
@ -1266,27 +1273,32 @@ unsigned int CDECL _control87(unsigned int newval, unsigned int mask)
case 0x8000: flags |= MSVCRT__DN_SAVE_OPERANDS_FLUSH_RESULTS; break;
case 0x8040: flags |= MSVCRT__DN_FLUSH; break;
}
old_flags = flags;
mask &= MSVCRT__MCW_EM | MSVCRT__MCW_RC | MSVCRT__MCW_DN;
flags = (flags & ~mask) | (newval & mask);
fpword = 0;
if (flags & MSVCRT__EM_INVALID) fpword |= 0x80;
if (flags & MSVCRT__EM_DENORMAL) fpword |= 0x100;
if (flags & MSVCRT__EM_ZERODIVIDE) fpword |= 0x200;
if (flags & MSVCRT__EM_OVERFLOW) fpword |= 0x400;
if (flags & MSVCRT__EM_UNDERFLOW) fpword |= 0x800;
if (flags & MSVCRT__EM_INEXACT) fpword |= 0x1000;
switch (flags & MSVCRT__MCW_RC)
if (flags != old_flags)
{
case MSVCRT__RC_CHOP: fpword |= 0x6000; break;
case MSVCRT__RC_UP: fpword |= 0x4000; break;
case MSVCRT__RC_DOWN: fpword |= 0x2000; break;
fpword = 0;
if (flags & MSVCRT__EM_INVALID) fpword |= 0x80;
if (flags & MSVCRT__EM_DENORMAL) fpword |= 0x100;
if (flags & MSVCRT__EM_ZERODIVIDE) fpword |= 0x200;
if (flags & MSVCRT__EM_OVERFLOW) fpword |= 0x400;
if (flags & MSVCRT__EM_UNDERFLOW) fpword |= 0x800;
if (flags & MSVCRT__EM_INEXACT) fpword |= 0x1000;
switch (flags & MSVCRT__MCW_RC)
{
case MSVCRT__RC_CHOP: fpword |= 0x6000; break;
case MSVCRT__RC_UP: fpword |= 0x4000; break;
case MSVCRT__RC_DOWN: fpword |= 0x2000; break;
}
switch (flags & MSVCRT__MCW_DN)
{
case MSVCRT__DN_FLUSH_OPERANDS_SAVE_RESULTS: fpword |= 0x0040; break;
case MSVCRT__DN_SAVE_OPERANDS_FLUSH_RESULTS: fpword |= 0x8000; break;
case MSVCRT__DN_FLUSH: fpword |= 0x8040; break;
}
__asm__ __volatile__( "ldmxcsr %0" :: "m" (fpword) );
}
switch (flags & MSVCRT__MCW_DN)
{
case MSVCRT__DN_FLUSH_OPERANDS_SAVE_RESULTS: fpword |= 0x0040; break;
case MSVCRT__DN_SAVE_OPERANDS_FLUSH_RESULTS: fpword |= 0x8000; break;
case MSVCRT__DN_FLUSH: fpword |= 0x8040; break;
}
__asm__ __volatile__( "ldmxcsr %0" :: "m" (fpword) );
#elif defined(__aarch64__)
unsigned long fpcr;

View File

@ -134,6 +134,8 @@ extern "C" {
#define _CW_DEFAULT (_RC_NEAR + _PC_64 + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL)
#endif
unsigned int __cdecl _clearfp(void);
unsigned int __cdecl _statusfp(void);
_ACRTIMP int __cdecl __fpe_flt_rounds(void);
unsigned int __cdecl _control87(unsigned int, unsigned int);
unsigned int __cdecl _controlfp(unsigned int, unsigned int);