1 /* 2 +----------------------------------------------------------------------+ 3 | Zend Engine | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 2.00 of the Zend license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.zend.com/license/2_00.txt. | 11 | If you did not receive a copy of the Zend license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@zend.com so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Christian Seiler <chris_se@gmx.net> | 16 +----------------------------------------------------------------------+ 17 */ 18 19 #ifndef ZEND_FLOAT_H 20 #define ZEND_FLOAT_H 21 22 BEGIN_EXTERN_C() 23 24 /* 25 Define functions for FP initialization and de-initialization. 26 */ 27 extern ZEND_API void zend_init_fpu(void); 28 extern ZEND_API void zend_shutdown_fpu(void); 29 extern ZEND_API void zend_ensure_fpu_mode(void); 30 31 END_EXTERN_C() 32 33 /* Copy of the contents of xpfpa.h (which is under public domain) 34 See http://wiki.php.net/rfc/rounding for details. 35 36 Cross Platform Floating Point Arithmetics 37 38 This header file defines several platform-dependent macros that ensure 39 equal and deterministic floating point behaviour across several platforms, 40 compilers and architectures. 41 42 The current macros are currently only used on x86 and x86_64 architectures, 43 on every other architecture, these macros expand to NOPs. This assumes that 44 other architectures do not have an internal precision and the operhand types 45 define the computational precision of floating point operations. This 46 assumption may be false, in that case, the author is interested in further 47 details on the other platform. 48 49 For further details, please visit: 50 http://www.christian-seiler.de/projekte/fpmath/ 51 52 Version: 20090317 */ 53 54 /* 55 Implementation notes: 56 57 x86_64: 58 - Since all x86_64 compilers use SSE by default, it is probably unnecessary 59 to use these macros there. We define them anyway since we are too lazy 60 to differentiate the architecture. Also, the compiler option -mfpmath=i387 61 justifies this decision. 62 63 General: 64 - It would be nice if one could detect whether SSE if used for math via some 65 funky compiler defines and if so, make the macros go to NOPs. Any ideas 66 on how to do that? 67 68 MS Visual C: 69 - Since MSVC users tipically don't use autoconf or CMake, we will detect 70 MSVC via compile time define. Floating point precision change isn't 71 supported on 64 bit platforms, so it's NOP. See 72 http://msdn.microsoft.com/en-us/library/c9676k6h(v=vs.110).aspx 73 */ 74 75 /* MSVC detection (MSVC people usually don't use autoconf) */ 76 #if defined(_MSC_VER) && !defined(_WIN64) 77 # define HAVE__CONTROLFP_S 78 #endif /* _MSC_VER */ 79 80 #ifdef HAVE__CONTROLFP_S 81 82 /* float.h defines _controlfp_s */ 83 # include <float.h> 84 85 # define XPFPA_HAVE_CW 1 86 # define XPFPA_CW_DATATYPE \ 87 unsigned int 88 89 # define XPFPA_STORE_CW(vptr) do { \ 90 _controlfp_s((unsigned int *)(vptr), 0, 0); \ 91 } while (0) 92 93 # define XPFPA_RESTORE_CW(vptr) do { \ 94 unsigned int _xpfpa_fpu_cw; \ 95 _controlfp_s(&_xpfpa_fpu_cw, *((unsigned int *)(vptr)), _MCW_PC); \ 96 } while (0) 97 98 # define XPFPA_DECLARE \ 99 unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw; 100 101 # define XPFPA_SWITCH_DOUBLE() do { \ 102 _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \ 103 _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \ 104 _controlfp_s(&_xpfpa_fpu_cw, _PC_53, _MCW_PC); \ 105 } while (0) 106 # define XPFPA_SWITCH_SINGLE() do { \ 107 _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \ 108 _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \ 109 _controlfp_s(&_xpfpa_fpu_cw, _PC_24, _MCW_PC); \ 110 } while (0) 111 /* NOTE: This only sets internal precision. MSVC does NOT support double- 112 extended precision! */ 113 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 114 _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \ 115 _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \ 116 _controlfp_s(&_xpfpa_fpu_cw, _PC_64, _MCW_PC); \ 117 } while (0) 118 # define XPFPA_RESTORE() \ 119 _controlfp_s(&_xpfpa_fpu_cw, _xpfpa_fpu_oldcw, _MCW_PC) 120 /* We do NOT use the volatile return trick since _controlfp_s is a function 121 call and thus FP registers are saved in memory anyway. However, we do use 122 a variable to ensure that the expression passed into val will be evaluated 123 *before* switching back contexts. */ 124 # define XPFPA_RETURN_DOUBLE(val) \ 125 do { \ 126 double _xpfpa_result = (val); \ 127 XPFPA_RESTORE(); \ 128 return _xpfpa_result; \ 129 } while (0) 130 # define XPFPA_RETURN_SINGLE(val) \ 131 do { \ 132 float _xpfpa_result = (val); \ 133 XPFPA_RESTORE(); \ 134 return _xpfpa_result; \ 135 } while (0) 136 /* This won't work, but we add a macro for it anyway. */ 137 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 138 do { \ 139 long double _xpfpa_result = (val); \ 140 XPFPA_RESTORE(); \ 141 return _xpfpa_result; \ 142 } while (0) 143 144 #elif defined(HAVE__CONTROLFP) 145 146 /* float.h defines _controlfp */ 147 # include <float.h> 148 149 # define XPFPA_DECLARE \ 150 unsigned int _xpfpa_fpu_oldcw; 151 152 # define XPFPA_HAVE_CW 1 153 # define XPFPA_CW_DATATYPE \ 154 unsigned int 155 156 # define XPFPA_STORE_CW(vptr) do { \ 157 *((unsigned int *)(vptr)) = _controlfp(0, 0); \ 158 } while (0) 159 160 # define XPFPA_RESTORE_CW(vptr) do { \ 161 _controlfp(*((unsigned int *)(vptr)), _MCW_PC); \ 162 } while (0) 163 164 # define XPFPA_SWITCH_DOUBLE() do { \ 165 _xpfpa_fpu_oldcw = _controlfp(0, 0); \ 166 _controlfp(_PC_53, _MCW_PC); \ 167 } while (0) 168 # define XPFPA_SWITCH_SINGLE() do { \ 169 _xpfpa_fpu_oldcw = _controlfp(0, 0); \ 170 _controlfp(_PC_24, _MCW_PC); \ 171 } while (0) 172 /* NOTE: This will only work as expected on MinGW. */ 173 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 174 _xpfpa_fpu_oldcw = _controlfp(0, 0); \ 175 _controlfp(_PC_64, _MCW_PC); \ 176 } while (0) 177 # define XPFPA_RESTORE() \ 178 _controlfp(_xpfpa_fpu_oldcw, _MCW_PC) 179 /* We do NOT use the volatile return trick since _controlfp is a function 180 call and thus FP registers are saved in memory anyway. However, we do use 181 a variable to ensure that the expression passed into val will be evaluated 182 *before* switching back contexts. */ 183 # define XPFPA_RETURN_DOUBLE(val) \ 184 do { \ 185 double _xpfpa_result = (val); \ 186 XPFPA_RESTORE(); \ 187 return _xpfpa_result; \ 188 } while (0) 189 # define XPFPA_RETURN_SINGLE(val) \ 190 do { \ 191 float _xpfpa_result = (val); \ 192 XPFPA_RESTORE(); \ 193 return _xpfpa_result; \ 194 } while (0) 195 /* This will only work on MinGW */ 196 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 197 do { \ 198 long double _xpfpa_result = (val); \ 199 XPFPA_RESTORE(); \ 200 return _xpfpa_result; \ 201 } while (0) 202 203 #elif defined(HAVE__FPU_SETCW) /* glibc systems */ 204 205 /* fpu_control.h defines _FPU_[GS]ETCW */ 206 # include <fpu_control.h> 207 208 # define XPFPA_DECLARE \ 209 fpu_control_t _xpfpa_fpu_oldcw, _xpfpa_fpu_cw; 210 211 # define XPFPA_HAVE_CW 1 212 # define XPFPA_CW_DATATYPE \ 213 fpu_control_t 214 215 # define XPFPA_STORE_CW(vptr) do { \ 216 _FPU_GETCW((*((fpu_control_t *)(vptr)))); \ 217 } while (0) 218 219 # define XPFPA_RESTORE_CW(vptr) do { \ 220 _FPU_SETCW((*((fpu_control_t *)(vptr)))); \ 221 } while (0) 222 223 # define XPFPA_SWITCH_DOUBLE() do { \ 224 _FPU_GETCW(_xpfpa_fpu_oldcw); \ 225 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE; \ 226 _FPU_SETCW(_xpfpa_fpu_cw); \ 227 } while (0) 228 # define XPFPA_SWITCH_SINGLE() do { \ 229 _FPU_GETCW(_xpfpa_fpu_oldcw); \ 230 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_DOUBLE) | _FPU_SINGLE; \ 231 _FPU_SETCW(_xpfpa_fpu_cw); \ 232 } while (0) 233 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 234 _FPU_GETCW(_xpfpa_fpu_oldcw); \ 235 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_SINGLE & ~_FPU_DOUBLE) | _FPU_EXTENDED; \ 236 _FPU_SETCW(_xpfpa_fpu_cw); \ 237 } while (0) 238 # define XPFPA_RESTORE() \ 239 _FPU_SETCW(_xpfpa_fpu_oldcw) 240 /* We use a temporary volatile variable (in a new block) in order to ensure 241 that the optimizer does not mis-optimize the instructions. Also, a volatile 242 variable ensures truncation to correct precision. */ 243 # define XPFPA_RETURN_DOUBLE(val) \ 244 do { \ 245 volatile double _xpfpa_result = (val); \ 246 XPFPA_RESTORE(); \ 247 return _xpfpa_result; \ 248 } while (0) 249 # define XPFPA_RETURN_SINGLE(val) \ 250 do { \ 251 volatile float _xpfpa_result = (val); \ 252 XPFPA_RESTORE(); \ 253 return _xpfpa_result; \ 254 } while (0) 255 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 256 do { \ 257 volatile long double _xpfpa_result = (val); \ 258 XPFPA_RESTORE(); \ 259 return _xpfpa_result; \ 260 } while (0) 261 262 #elif defined(HAVE_FPSETPREC) /* FreeBSD */ 263 264 /* fpu_control.h defines _FPU_[GS]ETCW */ 265 # include <machine/ieeefp.h> 266 267 # define XPFPA_DECLARE \ 268 fp_prec_t _xpfpa_fpu_oldprec; 269 270 # define XPFPA_HAVE_CW 1 271 # define XPFPA_CW_DATATYPE \ 272 fp_prec_t 273 274 # define XPFPA_STORE_CW(vptr) do { \ 275 *((fp_prec_t *)(vptr)) = fpgetprec(); \ 276 } while (0) 277 278 # define XPFPA_RESTORE_CW(vptr) do { \ 279 fpsetprec(*((fp_prec_t *)(vptr))); \ 280 } while (0) 281 282 # define XPFPA_SWITCH_DOUBLE() do { \ 283 _xpfpa_fpu_oldprec = fpgetprec(); \ 284 fpsetprec(FP_PD); \ 285 } while (0) 286 # define XPFPA_SWITCH_SINGLE() do { \ 287 _xpfpa_fpu_oldprec = fpgetprec(); \ 288 fpsetprec(FP_PS); \ 289 } while (0) 290 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 291 _xpfpa_fpu_oldprec = fpgetprec(); \ 292 fpsetprec(FP_PE); \ 293 } while (0) 294 # define XPFPA_RESTORE() \ 295 fpsetprec(_xpfpa_fpu_oldprec) 296 /* We use a temporary volatile variable (in a new block) in order to ensure 297 that the optimizer does not mis-optimize the instructions. Also, a volatile 298 variable ensures truncation to correct precision. */ 299 # define XPFPA_RETURN_DOUBLE(val) \ 300 do { \ 301 volatile double _xpfpa_result = (val); \ 302 XPFPA_RESTORE(); \ 303 return _xpfpa_result; \ 304 } while (0) 305 # define XPFPA_RETURN_SINGLE(val) \ 306 do { \ 307 volatile float _xpfpa_result = (val); \ 308 XPFPA_RESTORE(); \ 309 return _xpfpa_result; \ 310 } while (0) 311 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 312 do { \ 313 volatile long double _xpfpa_result = (val); \ 314 XPFPA_RESTORE(); \ 315 return _xpfpa_result; \ 316 } while (0) 317 318 #elif defined(HAVE_FPU_INLINE_ASM_X86) 319 320 /* 321 Custom x86 inline assembler implementation. 322 323 This implementation does not use predefined wrappers of the OS / compiler 324 but rather uses x86/x87 inline assembler directly. Basic instructions: 325 326 fnstcw - Store the FPU control word in a variable 327 fldcw - Load the FPU control word from a variable 328 329 Bits (only bits 8 and 9 are relevant, bits 0 to 7 are for other things): 330 0x0yy: Single precision 331 0x1yy: Reserved 332 0x2yy: Double precision 333 0x3yy: Double-extended precision 334 335 We use an unsigned int for the datatype. glibc sources add __mode__ (__HI__) 336 attribute to it (HI stands for half-integer according to docs). It is unclear 337 what the does exactly and how portable it is. 338 339 The assembly syntax works with GNU CC, Intel CC and Sun CC. 340 */ 341 342 # define XPFPA_DECLARE \ 343 unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw; 344 345 # define XPFPA_HAVE_CW 1 346 # define XPFPA_CW_DATATYPE \ 347 unsigned int 348 349 # define XPFPA_STORE_CW(vptr) do { \ 350 __asm__ __volatile__ ("fnstcw %0" : "=m" (*((unsigned int *)(vptr)))); \ 351 } while (0) 352 353 # define XPFPA_RESTORE_CW(vptr) do { \ 354 __asm__ __volatile__ ("fldcw %0" : : "m" (*((unsigned int *)(vptr)))); \ 355 } while (0) 356 357 # define XPFPA_SWITCH_DOUBLE() do { \ 358 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \ 359 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x100) | 0x200; \ 360 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \ 361 } while (0) 362 # define XPFPA_SWITCH_SINGLE() do { \ 363 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \ 364 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x300); \ 365 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \ 366 } while (0) 367 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 368 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \ 369 _xpfpa_fpu_cw = _xpfpa_fpu_oldcw | 0x300; \ 370 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \ 371 } while (0) 372 # define XPFPA_RESTORE() \ 373 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_oldcw)) 374 /* We use a temporary volatile variable (in a new block) in order to ensure 375 that the optimizer does not mis-optimize the instructions. Also, a volatile 376 variable ensures truncation to correct precision. */ 377 # define XPFPA_RETURN_DOUBLE(val) \ 378 do { \ 379 volatile double _xpfpa_result = (val); \ 380 XPFPA_RESTORE(); \ 381 return _xpfpa_result; \ 382 } while (0) 383 # define XPFPA_RETURN_SINGLE(val) \ 384 do { \ 385 volatile float _xpfpa_result = (val); \ 386 XPFPA_RESTORE(); \ 387 return _xpfpa_result; \ 388 } while (0) 389 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 390 do { \ 391 volatile long double _xpfpa_result = (val); \ 392 XPFPA_RESTORE(); \ 393 return _xpfpa_result; \ 394 } while (0) 395 396 #else /* FPU CONTROL */ 397 398 /* 399 This is either not an x87 FPU or the inline assembly syntax was not 400 recognized. In any case, default to NOPs for the macros and hope the 401 generated code will behave as planned. 402 */ 403 # define XPFPA_DECLARE /* NOP */ 404 # define XPFPA_HAVE_CW 0 405 # define XPFPA_CW_DATATYPE unsigned int 406 # define XPFPA_STORE_CW(variable) /* NOP */ 407 # define XPFPA_RESTORE_CW(variable) /* NOP */ 408 # define XPFPA_SWITCH_DOUBLE() /* NOP */ 409 # define XPFPA_SWITCH_SINGLE() /* NOP */ 410 # define XPFPA_SWITCH_DOUBLE_EXTENDED() /* NOP */ 411 # define XPFPA_RESTORE() /* NOP */ 412 # define XPFPA_RETURN_DOUBLE(val) return (val) 413 # define XPFPA_RETURN_SINGLE(val) return (val) 414 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) return (val) 415 416 #endif /* FPU CONTROL */ 417 418 #endif 419 420 /* 421 * Local variables: 422 * tab-width: 4 423 * c-basic-offset: 4 424 * indent-tabs-mode: t 425 * End: 426 * vim600: sw=4 ts=4 fdm=marker 427 * vim<600: sw=4 ts=4 428 */ 429