diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index b476e44cfc2..bd9fe7fecef 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -22,6 +22,7 @@ #include #include #include +#include /* 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[] = diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c index eae917076ff..5a4126ad583 100644 --- a/dlls/msvcrt/math.c +++ b/dlls/msvcrt/math.c @@ -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; diff --git a/include/msvcrt/float.h b/include/msvcrt/float.h index 97b2fbee13a..06eadec0ffa 100644 --- a/include/msvcrt/float.h +++ b/include/msvcrt/float.h @@ -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);