1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: MIT (https://spdx.org/licenses/MIT) 4 * PURPOSE: Tests for fp control functions 5 * COPYRIGHT: Copyright 2022 Timo Kreuzer <timo.kreuzer@reactos.org> 6 */ 7 8 #include <ntstatus.h> 9 #define WIN32_NO_STATUS 10 #include <windows.h> 11 12 #include <apitest.h> 13 #include <xmmintrin.h> 14 #include <float.h> 15 #include <pseh/pseh2.h> 16 17 18 unsigned int get_native_fpcw(void) 19 { 20 #ifdef _M_AMD64 21 return _mm_getcsr(); 22 #elif defined (_M_IX86) 23 unsigned short fpcw; 24 #if defined(_MSC_VER) 25 __asm fstsw[fpcw]; 26 #else 27 __asm__ __volatile__("fstsw %0" : "=m" (fpcw) : ); 28 #endif 29 return fpcw; 30 #else 31 #error "Unsupported architecture" 32 return 0; 33 #endif 34 } 35 36 void set_native_fpcw(unsigned int value) 37 { 38 #ifdef _M_AMD64 39 _mm_setcsr(value); 40 #elif defined (_M_IX86) 41 unsigned short fpcw = (unsigned short)value; 42 #if defined(_MSC_VER) 43 __asm fldcw[fpcw]; 44 #else 45 __asm__ __volatile__("fldcw %0" : : "m" (fpcw)); 46 #endif 47 #else 48 #error "Unsupported architecture" 49 #endif 50 } 51 52 /* 53 _clear87 54 _clearfp 55 _controlfp_s 56 _set_controlfp 57 _statusfp 58 __control87_2 59 */ 60 61 #ifdef _M_IX86 62 #define ON_IX86(x) x 63 #else 64 #define ON_IX86(x) 65 #endif 66 67 #ifdef _M_AMD64 68 #define ON_AMD64(x) x 69 #else 70 #define ON_AMD64(x) 71 #endif 72 73 #ifdef _M_ARM 74 #define ON_ARM(x) x 75 #else 76 #define ON_ARM(x) 77 #endif 78 79 struct 80 { 81 unsigned int Value; 82 unsigned int Mask; 83 unsigned int Result; 84 unsigned int Native; 85 } g_controlfp_Testcases[] = 86 { 87 { 0xffffffff, 0xffffffff, ON_IX86(0x30e031f) ON_AMD64(0x308031f) ON_ARM(0), ON_IX86(0) ON_AMD64(0xff80) ON_ARM(0) }, 88 { 0, 0xffffffff, 0x80000, ON_IX86(0) ON_AMD64(0x100) ON_ARM(0) }, 89 { 0xffffffff, 0x14, 0x80014, ON_IX86(0) ON_AMD64(0x580) ON_ARM(0) }, 90 { _EM_INEXACT, 0xffffffff, _EM_INEXACT | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_INEXACT | _MM_MASK_DENORM) ON_ARM(0) }, 91 { _EM_UNDERFLOW, 0xffffffff, _EM_UNDERFLOW | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_UNDERFLOW | _MM_MASK_DENORM) ON_ARM(0) }, 92 { _EM_OVERFLOW, 0xffffffff, _EM_OVERFLOW | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_OVERFLOW | _MM_MASK_DENORM) ON_ARM(0) }, 93 { _EM_ZERODIVIDE, 0xffffffff, _EM_ZERODIVIDE | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_DIV_ZERO | _MM_MASK_DENORM) ON_ARM(0) }, 94 { _EM_INVALID, 0xffffffff, _EM_INVALID | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_INVALID | _MM_MASK_DENORM) ON_ARM(0) }, 95 { _RC_NEAR, 0xffffffff, _RC_NEAR | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_ROUND_NEAREST | _MM_MASK_DENORM) ON_ARM(0) }, 96 { _RC_DOWN, 0xffffffff, _RC_DOWN | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_ROUND_DOWN | _MM_MASK_DENORM) ON_ARM(0) }, 97 { _RC_UP, 0xffffffff, _RC_UP | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_ROUND_UP | _MM_MASK_DENORM) ON_ARM(0) }, 98 { _RC_CHOP, 0xffffffff, _RC_CHOP | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_ROUND_TOWARD_ZERO | _MM_MASK_DENORM) ON_ARM(0) }, 99 { _IC_AFFINE, 0xffffffff, _EM_DENORMAL ON_IX86(| _IC_AFFINE), ON_IX86(0) ON_AMD64(_MM_MASK_DENORM) ON_ARM(0)}, 100 { _IC_PROJECTIVE, 0xffffffff, _IC_PROJECTIVE | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_DENORM) ON_ARM(0) }, 101 { _DN_SAVE, 0xffffffff, _DN_SAVE | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_MASK_DENORM) ON_ARM(0) }, 102 { _DN_FLUSH, 0xffffffff, _DN_FLUSH | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_FLUSH_ZERO_ON | 0x40 | _MM_MASK_DENORM) ON_ARM(0) }, 103 { _DN_FLUSH_OPERANDS_SAVE_RESULTS, 0xffffffff, _DN_FLUSH_OPERANDS_SAVE_RESULTS | _EM_DENORMAL, ON_IX86(0) ON_AMD64(0x40 | _MM_MASK_DENORM) ON_ARM(0) }, 104 { _DN_SAVE_OPERANDS_FLUSH_RESULTS, 0xffffffff, _DN_SAVE_OPERANDS_FLUSH_RESULTS | _EM_DENORMAL, ON_IX86(0) ON_AMD64(_MM_FLUSH_ZERO_ON | _MM_MASK_DENORM) ON_ARM(0) }, 105 }; 106 107 void Test_controlfp(void) 108 { 109 unsigned int i, native_fpcw, fpcw; 110 111 for (i = 0; i < _countof(g_controlfp_Testcases); i++) 112 { 113 fpcw = _controlfp(g_controlfp_Testcases[i].Value, g_controlfp_Testcases[i].Mask); 114 ok(fpcw == g_controlfp_Testcases[i].Result, "[%u] _controlfp failed: expected 0x%x, got 0x%x\n", i, g_controlfp_Testcases[i].Result, fpcw); 115 native_fpcw = get_native_fpcw(); 116 ok(native_fpcw == g_controlfp_Testcases[i].Native, "[%u] wrong native_fpcw: expected 0x%x, got 0x%x\n", i, g_controlfp_Testcases[i].Native, native_fpcw); 117 } 118 119 /* Restore sane state */ 120 _fpreset(); 121 } 122 123 #if defined(_M_IX86) || defined(_M_AMD64) 124 void Test_control87(void) 125 { 126 unsigned int native_fpcw, fpcw; 127 128 fpcw = _control87(0, 0xffffffff); 129 ok(fpcw == 0, "_control87 failed: expected 0x%x, got 0x%x\n", 0, fpcw); 130 native_fpcw = get_native_fpcw(); 131 ok_hex(native_fpcw, ON_IX86(0) ON_AMD64(0)); 132 133 /* Restore sane state */ 134 _fpreset(); 135 } 136 #endif 137 138 typedef enum _FP_OP 139 { 140 OP_Inexact, 141 OP_Underflow, 142 OP_Overflow, 143 OP_ZeroDivide, 144 OP_Invalid, 145 OP_Denormal 146 } FP_OP; 147 148 struct 149 { 150 FP_OP Operation; 151 unsigned int Fpcw; 152 unsigned int FpStatus; 153 unsigned int ExceptionCode; 154 unsigned int Native; 155 } g_exception_Testcases[] = 156 { 157 { OP_Inexact, 0xffffffff, _SW_UNDERFLOW | _SW_INEXACT ON_IX86(| _SW_DENORMAL), 0, ON_IX86(0x32) ON_AMD64(0xffb0) ON_ARM(0)}, 158 { OP_Inexact, ~_EM_INEXACT, _SW_INEXACT ON_AMD64(| _SW_UNDERFLOW), STATUS_FLOAT_INEXACT_RESULT, ON_IX86(0x3800) ON_AMD64(0xefb0) ON_ARM(0)}, 159 { OP_Inexact, ~_MCW_EM, _SW_INEXACT ON_AMD64(| _SW_UNDERFLOW), ON_IX86(STATUS_FLOAT_INEXACT_RESULT) ON_AMD64(STATUS_FLOAT_UNDERFLOW) ON_ARM(STATUS_FLOAT_UNDERFLOW), ON_IX86(0x3800) ON_AMD64(0xe130) ON_ARM(0) }, 160 { OP_Underflow, 0xffffffff, _SW_UNDERFLOW | _SW_INEXACT, 0, ON_IX86(0x30) ON_AMD64(0xffb0) ON_ARM(0)}, 161 { OP_Underflow, ~_EM_UNDERFLOW, _SW_UNDERFLOW | _SW_INEXACT, STATUS_FLOAT_UNDERFLOW, ON_IX86(0x3800) ON_AMD64(0xf7b0) ON_ARM(0) }, 162 { OP_Underflow, ~_MCW_EM, _SW_INEXACT ON_AMD64(| _SW_UNDERFLOW), ON_IX86(STATUS_FLOAT_INEXACT_RESULT) ON_AMD64(STATUS_FLOAT_UNDERFLOW) ON_ARM(STATUS_FLOAT_UNDERFLOW), ON_IX86(0x3800) ON_AMD64(0xe130) ON_ARM(0) }, 163 { OP_Overflow, 0xffffffff, _SW_OVERFLOW | _SW_INEXACT, 0, ON_IX86(0x28) ON_AMD64(0xffa8) ON_ARM(0) }, 164 { OP_Overflow, ~_EM_OVERFLOW, _SW_OVERFLOW | _SW_INEXACT, STATUS_FLOAT_OVERFLOW, ON_IX86(0x3800) ON_AMD64(0xfba8) ON_ARM(0) }, 165 { OP_Overflow, ~_MCW_EM, _SW_INEXACT ON_AMD64(| _SW_OVERFLOW), ON_IX86(STATUS_FLOAT_INEXACT_RESULT) ON_AMD64(STATUS_FLOAT_OVERFLOW) ON_ARM(STATUS_FLOAT_OVERFLOW), ON_IX86(0x3800) ON_AMD64(0xe128) ON_ARM(0)}, 166 { OP_ZeroDivide, 0xffffffff, _SW_ZERODIVIDE, 0, ON_IX86(0x4) ON_AMD64(0xff84) ON_ARM(0) }, 167 { OP_ZeroDivide, ~_EM_ZERODIVIDE, _SW_ZERODIVIDE, STATUS_FLOAT_DIVIDE_BY_ZERO, ON_IX86(0x3000) ON_AMD64(0xfd84) ON_ARM(0) }, 168 { OP_ZeroDivide, ~_MCW_EM, _SW_ZERODIVIDE, STATUS_FLOAT_DIVIDE_BY_ZERO, ON_IX86(0x3000) ON_AMD64(0xe104) ON_ARM(0) }, 169 { OP_Invalid, 0xffffffff, _SW_INVALID, 0, ON_IX86(0x1) ON_AMD64(0xff81) ON_ARM(0) }, 170 { OP_Invalid, ~_EM_INVALID, _SW_INVALID, STATUS_FLOAT_INVALID_OPERATION, ON_IX86(0) ON_AMD64(0xff01) ON_ARM(0) }, 171 { OP_Invalid, ~_MCW_EM, _SW_INVALID, STATUS_FLOAT_INVALID_OPERATION, ON_IX86(0) ON_AMD64(0xe101) ON_ARM(0) }, 172 #if defined(_M_IX86) || defined(_M_AMD64) // || defined(_M_ARM64) ? 173 { OP_Denormal, 0xffffffff, _SW_DENORMAL | _SW_INEXACT ON_AMD64(| _SW_UNDERFLOW), 0, ON_IX86(0x22) ON_AMD64(0xffb2) ON_ARM(0)}, 174 { OP_Denormal, ~_EM_DENORMAL, _SW_DENORMAL, STATUS_FLOAT_INVALID_OPERATION, ON_IX86(0x3800) ON_AMD64(0xfe82) ON_ARM(0) }, 175 { OP_Denormal, ~_MCW_EM, _SW_DENORMAL, STATUS_FLOAT_INVALID_OPERATION, ON_IX86(0x3800) ON_AMD64(0xe002) ON_ARM(0) }, 176 #endif 177 }; 178 179 void Test_exceptions(void) 180 { 181 volatile double a, b; 182 unsigned long long ull; 183 volatile long status = 0; 184 185 unsigned int i, exp_fpstatus, native_fpcw, statusfp; 186 187 for (i = 0; i < _countof(g_exception_Testcases); i++) 188 { 189 /* Start clean */ 190 status = 0; 191 _fpreset(); 192 _clearfp(); 193 ok_hex(_statusfp(), 0); 194 195 _controlfp(g_exception_Testcases[i].Fpcw, 0xffffffff); 196 #if defined(_M_IX86) || defined(_M_AMD64) // || defined(_M_ARM64) ? 197 if (g_exception_Testcases[i].Operation == OP_Denormal) 198 _control87(g_exception_Testcases[i].Fpcw, 0xffffffff); 199 #endif 200 201 _SEH2_TRY 202 { 203 switch (g_exception_Testcases[i].Operation) 204 { 205 case OP_Inexact: 206 a = 1e-40; 207 b = (float)(a + 1e-40); 208 break; 209 case OP_Underflow: 210 a = DBL_MIN; 211 b = a / 3.0e16; 212 break; 213 case OP_Overflow: 214 a = DBL_MAX; 215 b = a * 3.0; 216 break; 217 case OP_ZeroDivide: 218 a = 0.0; 219 b = 1.0 / a; 220 break; 221 case OP_Invalid: 222 ull = 0x7FF0000000000001ull; 223 a = *(double*)&ull; 224 b = a * 2; 225 break; 226 case OP_Denormal: 227 a = DBL_MIN; 228 b = a - 4.9406564584124654e-324; 229 break; 230 default: 231 (void)b; 232 } 233 native_fpcw = get_native_fpcw(); 234 statusfp = _clearfp(); 235 } 236 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 237 { 238 #ifdef _M_IX86 239 /* On x86 we need to clear before doing any other fp operations, otherwise it will throw again */ 240 statusfp = _clearfp(); 241 native_fpcw = get_native_fpcw(); 242 #else 243 native_fpcw = get_native_fpcw(); 244 statusfp = _clearfp(); 245 #endif 246 status = _SEH2_GetExceptionCode(); 247 } 248 _SEH2_END; 249 250 exp_fpstatus = g_exception_Testcases[i].FpStatus; 251 ok(statusfp == exp_fpstatus, "[%u] Wrong value for _statusfp(). Expected 0x%lx, got 0x%lx\n", i, exp_fpstatus, statusfp); 252 ok(status == g_exception_Testcases[i].ExceptionCode, "[%u] Wrong value for status. Expected 0x%lx, got 0x%lx\n", i, g_exception_Testcases[i].ExceptionCode, status); 253 ok(native_fpcw == g_exception_Testcases[i].Native, "[%u] wrong native_fpcw: expected 0x%x, got 0x%x\n", i, g_exception_Testcases[i].Native, native_fpcw); 254 } 255 } 256 257 START_TEST(fpcontrol) 258 { 259 unsigned int native_fpcw, fpcw, fpstatus; 260 261 /* Test native start fpcw */ 262 native_fpcw = get_native_fpcw(); 263 ok_hex(native_fpcw, ON_IX86(0) ON_AMD64(0x1f80) ON_ARM(0) ); 264 265 /* Test start fpcw */ 266 fpcw = _controlfp(0, 0); 267 ok_hex(fpcw, ON_IX86(0x9001f) ON_AMD64(0x8001f) ON_ARM(0)); 268 269 /* Test start status */ 270 fpstatus = _statusfp(); 271 ok_hex(fpstatus, 0); 272 273 /* Test _fpreset */ 274 fpcw = _controlfp(0, 0xffffffff); 275 ok_hex(fpcw, 0x80000); 276 _fpreset(); 277 fpcw = _controlfp(0, 0); 278 ok_hex(fpcw, ON_IX86(0x9001f) ON_AMD64(0x8001f) ON_ARM(0)); 279 280 Test_controlfp(); 281 #if defined(_M_IX86) || defined(_M_AMD64) 282 Test_control87(); 283 #endif 284 Test_exceptions(); 285 } 286