[PATCH] msvcrt: Don't set MXCSR register in __control87_2 if unchanged.
Myah Caron
qsniyg at protonmail.com
Fri Jul 3 13:34:52 CDT 2020
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=27594
Signed-off-by: Myah Caron <qsniyg at protonmail.com>
---
I've only tested on x86 as I believe x64 is different enough to warrant a separate commit if the same issue is also present there.
Though I suspect the x87 control word might need the same treatment, I couldn't create a test case that displayed a requirement for this, so I've left it untouched.
---
dlls/kernel32/tests/thread.c | 36 +++++++++++++++++++++++++-----
dlls/msvcrt/math.c | 43 ++++++++++++++++++++----------------
2 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c
index b476e44cfc..9b12b5043d 100644
--- a/dlls/kernel32/tests/thread.c
+++ b/dlls/kernel32/tests/thread.c
@@ -1829,6 +1829,19 @@ static inline unsigned long get_fpu_cw(void)
#endif
}
+static inline void set_fpu_cw(unsigned int cw, unsigned int sse)
+{
+#if defined(__i386__) || defined(__x86_64__)
+#ifdef _MSC_VER
+ __asm { fldcw [cw] }
+ __asm { ldmxcsr [sse] }
+#else
+ __asm__ volatile ("fldcw %0" : : "m" (cw));
+ __asm__ volatile ("ldmxcsr %0" : : "m" (sse));
+#endif
+#endif
+}
+
static DWORD WINAPI fpu_thread(void *param)
{
struct fpu_thread_ctx *ctx = param;
@@ -1870,7 +1883,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[7] =
{
#ifdef __i386__
{ _MCW_EM | _PC_53, MAKELONG( 0x27f, 0x1f80 ) },
@@ -1878,27 +1891,30 @@ 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_24 | _RC_DOWN, MAKELONG( 0x47f, 0x3fa1 ) }
#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 ) },
+ { 0xdeadbeef, 0xdeadbeef }
#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 },
+ { 0xdeadbeef, 0xdeadbeef }
#else
{ 0xdeadbeef, 0xdeadbeef }
#endif
};
unsigned int initial_cw, cw;
- unsigned long fpu_cw;
+ unsigned long initial_fpu_cw, fpu_cw;
fpu_cw = get_fpu_cw();
initial_cw = _control87( 0, 0 );
@@ -1933,6 +1949,16 @@ 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);
+
+#ifdef __i386__
+ initial_fpu_cw = get_fpu_cw();
+ set_fpu_cw(0x47f, 0x3fa1);
+ cw = _control87( _RC_DOWN, _MCW_RC );
+ 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);
+ set_fpu_cw(initial_fpu_cw & 0xffff, (initial_fpu_cw >> 16) & 0xffff);
+#endif
}
static const char manifest_dep[] =
diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index eae917076f..d11bd53acd 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,33 @@ 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;
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;
- }
- 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;
+ /* 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) );
}
- __asm__ __volatile__( "ldmxcsr %0" : : "m" (fpword) );
}
*sse2_cw = flags;
}
--
2.26.2
More information about the wine-devel
mailing list