Paul Gofman : msvcrt: Save and restore rounding mode in fegetenv() / fesetenv().

Alexandre Julliard julliard at winehq.org
Wed Nov 18 15:48:01 CST 2020


Module: wine
Branch: master
Commit: 3acd68b24171e7c4b9fd82b4449dbe879df29884
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=3acd68b24171e7c4b9fd82b4449dbe879df29884

Author: Paul Gofman <pgofman at codeweavers.com>
Date:   Tue Nov 17 23:07:49 2020 +0300

msvcrt: Save and restore rounding mode in fegetenv() / fesetenv().

Fixes Serious Sam 4 flickering in Vulkan mode.

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
Signed-off-by: Piotr Caban <piotr at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/msvcr120/tests/msvcr120.c | 23 ++++++++++++++++++++---
 dlls/msvcrt/math.c             | 19 +++++++++++++++----
 2 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c
index 52531ddad72..8663fc81ee8 100644
--- a/dlls/msvcr120/tests/msvcr120.c
+++ b/dlls/msvcr120/tests/msvcr120.c
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <float.h>
 #include <math.h>
+#include <fenv.h>
 #include <limits.h>
 #include <wctype.h>
 
@@ -183,6 +184,9 @@ static float (CDECL *p_wcstof)(const wchar_t*, wchar_t**);
 static double (CDECL *p_remainder)(double, double);
 static int* (CDECL *p_errno)(void);
 static int (CDECL *p_fegetenv)(fenv_t*);
+static int (CDECL *p_fesetenv)(const fenv_t*);
+static int (CDECL *p_fegetround)(void);
+static int (CDECL *p_fesetround)(int);
 static int (CDECL *p__clearfp)(void);
 static _locale_t (__cdecl *p_wcreate_locale)(int, const wchar_t *);
 static void (__cdecl *p_free_locale)(_locale_t);
@@ -254,6 +258,10 @@ static BOOL init(void)
     p_free_locale = (void*)GetProcAddress(module, "_free_locale");
     SET(p_wctype, "wctype");
     SET(p_fegetenv, "fegetenv");
+    SET(p_fesetenv, "fesetenv");
+    SET(p_fegetround, "fegetround");
+    SET(p_fesetround, "fesetround");
+
     SET(p__clearfp, "_clearfp");
     SET(p_vsscanf, "vsscanf");
     SET(p__Cbuild, "_Cbuild");
@@ -780,18 +788,27 @@ static void test_critical_section(void)
     call_func1(p_critical_section_dtor, &cs);
 }
 
-static void test_fegetenv(void)
+static void test_feenv(void)
 {
+    fenv_t env, env2;
     int ret;
-    fenv_t env;
 
     p__clearfp();
 
     ret = p_fegetenv(&env);
     ok(!ret, "fegetenv returned %x\n", ret);
+    p_fesetround(FE_UPWARD);
     ok(env.control == (_EM_INEXACT|_EM_UNDERFLOW|_EM_OVERFLOW|_EM_ZERODIVIDE|_EM_INVALID),
             "env.control = %x\n", env.control);
     ok(!env.status, "env.status = %x\n", env.status);
+    ret = p_fegetenv(&env2);
+    ok(!ret, "fegetenv returned %x\n", ret);
+    ok(env2.control == (_EM_INEXACT|_EM_UNDERFLOW|_EM_OVERFLOW|_EM_ZERODIVIDE|_EM_INVALID | FE_UPWARD),
+            "env2.control = %x\n", env2.control);
+    ret = p_fesetenv(&env);
+    ok(!ret, "fesetenv returned %x\n", ret);
+    ret = p_fegetround();
+    ok(ret == FE_TONEAREST, "Got unexpected round mode %#x.\n", ret);
 }
 
 static void test__wcreate_locale(void)
@@ -1110,7 +1127,7 @@ START_TEST(msvcr120)
     test__strtof();
     test_remainder();
     test_critical_section();
-    test_fegetenv();
+    test_feenv();
     test__wcreate_locale();
     test__Condition_variable();
     test_wctype();
diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index af6216e0ea8..25a9b5e096d 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -2074,7 +2074,7 @@ int CDECL _controlfp_s(unsigned int *cur, unsigned int newval, unsigned int mask
 int CDECL MSVCRT_fegetenv(MSVCRT_fenv_t *env)
 {
     env->control = _controlfp(0, 0) & (MSVCRT__EM_INEXACT | MSVCRT__EM_UNDERFLOW |
-            MSVCRT__EM_OVERFLOW | MSVCRT__EM_ZERODIVIDE | MSVCRT__EM_INVALID);
+            MSVCRT__EM_OVERFLOW | MSVCRT__EM_ZERODIVIDE | MSVCRT__EM_INVALID | MSVCRT__RC_CHOP);
     env->status = _statusfp();
     return 0;
 }
@@ -2193,12 +2193,18 @@ int CDECL MSVCRT_fesetenv(const MSVCRT_fenv_t *env)
 
     __asm__ __volatile__( "fnstenv %0" : "=m" (fenv) );
 
-    fenv.control_word &= ~0x3d;
+    fenv.control_word &= ~0xc3d;
     if (env->control & MSVCRT__EM_INVALID) fenv.control_word |= 0x1;
     if (env->control & MSVCRT__EM_ZERODIVIDE) fenv.control_word |= 0x4;
     if (env->control & MSVCRT__EM_OVERFLOW) fenv.control_word |= 0x8;
     if (env->control & MSVCRT__EM_UNDERFLOW) fenv.control_word |= 0x10;
     if (env->control & MSVCRT__EM_INEXACT) fenv.control_word |= 0x20;
+    switch (env->control & MSVCRT__MCW_RC)
+    {
+        case MSVCRT__RC_UP|MSVCRT__RC_DOWN: fenv.control_word |= 0xc00; break;
+        case MSVCRT__RC_UP:                 fenv.control_word |= 0x800; break;
+        case MSVCRT__RC_DOWN:               fenv.control_word |= 0x400; break;
+    }
 
     fenv.status_word &= ~0x3d;
     if (env->status & MSVCRT__SW_INVALID) fenv.status_word |= 0x1;
@@ -2213,14 +2219,19 @@ int CDECL MSVCRT_fesetenv(const MSVCRT_fenv_t *env)
     if (sse2_supported)
     {
         DWORD fpword;
-
         __asm__ __volatile__( "stmxcsr %0" : "=m" (fpword) );
-        fpword &= ~0x1e80;
+        fpword &= ~0x7e80;
         if (env->control & MSVCRT__EM_INVALID) fpword |= 0x80;
         if (env->control & MSVCRT__EM_ZERODIVIDE) fpword |= 0x200;
         if (env->control & MSVCRT__EM_OVERFLOW) fpword |= 0x400;
         if (env->control & MSVCRT__EM_UNDERFLOW) fpword |= 0x800;
         if (env->control & MSVCRT__EM_INEXACT) fpword |= 0x1000;
+        switch (env->control & MSVCRT__MCW_RC)
+        {
+            case MSVCRT__RC_CHOP: fpword |= 0x6000; break;
+            case MSVCRT__RC_UP:   fpword |= 0x4000; break;
+            case MSVCRT__RC_DOWN: fpword |= 0x2000; break;
+        }
         __asm__ __volatile__( "ldmxcsr %0" : : "m" (fpword) );
     }
 




More information about the wine-cvs mailing list