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