1 /* 2 +----------------------------------------------------------------------+ 3 | Zend Engine | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 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, we do not define these 59 macros there. We ignore the compiler option -mfpmath=i387, because there is 60 no reason to use it on x86_64. 61 62 General: 63 - It would be nice if one could detect whether SSE if used for math via some 64 funky compiler defines and if so, make the macros go to NOPs. Any ideas 65 on how to do that? 66 67 MS Visual C: 68 - Since MSVC users typically don't use autoconf or CMake, we will detect 69 MSVC via compile time define. 70 */ 71 72 /* MSVC detection (MSVC people usually don't use autoconf) */ 73 #if defined(_MSC_VER) && !defined(_WIN64) 74 # define HAVE__CONTROLFP_S 75 #endif /* _MSC_VER */ 76 77 #if defined(HAVE__CONTROLFP_S) && !defined(__x86_64__) 78 79 /* float.h defines _controlfp_s */ 80 # include <float.h> 81 82 # define XPFPA_HAVE_CW 1 83 # define XPFPA_CW_DATATYPE \ 84 unsigned int 85 86 # define XPFPA_STORE_CW(vptr) do { \ 87 _controlfp_s((unsigned int *)(vptr), 0, 0); \ 88 } while (0) 89 90 # define XPFPA_RESTORE_CW(vptr) do { \ 91 unsigned int _xpfpa_fpu_cw; \ 92 _controlfp_s(&_xpfpa_fpu_cw, *((unsigned int *)(vptr)), _MCW_PC); \ 93 } while (0) 94 95 # define XPFPA_DECLARE \ 96 unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw; 97 98 # define XPFPA_SWITCH_DOUBLE() do { \ 99 _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \ 100 _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \ 101 _controlfp_s(&_xpfpa_fpu_cw, _PC_53, _MCW_PC); \ 102 } while (0) 103 # define XPFPA_SWITCH_SINGLE() do { \ 104 _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \ 105 _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \ 106 _controlfp_s(&_xpfpa_fpu_cw, _PC_24, _MCW_PC); \ 107 } while (0) 108 /* NOTE: This only sets internal precision. MSVC does NOT support double- 109 extended precision! */ 110 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 111 _controlfp_s(&_xpfpa_fpu_cw, 0, 0); \ 112 _xpfpa_fpu_oldcw = _xpfpa_fpu_cw; \ 113 _controlfp_s(&_xpfpa_fpu_cw, _PC_64, _MCW_PC); \ 114 } while (0) 115 # define XPFPA_RESTORE() \ 116 _controlfp_s(&_xpfpa_fpu_cw, _xpfpa_fpu_oldcw, _MCW_PC) 117 /* We do NOT use the volatile return trick since _controlfp_s is a function 118 call and thus FP registers are saved in memory anyway. However, we do use 119 a variable to ensure that the expression passed into val will be evaluated 120 *before* switching back contexts. */ 121 # define XPFPA_RETURN_DOUBLE(val) \ 122 do { \ 123 double _xpfpa_result = (val); \ 124 XPFPA_RESTORE(); \ 125 return _xpfpa_result; \ 126 } while (0) 127 # define XPFPA_RETURN_SINGLE(val) \ 128 do { \ 129 float _xpfpa_result = (val); \ 130 XPFPA_RESTORE(); \ 131 return _xpfpa_result; \ 132 } while (0) 133 /* This won't work, but we add a macro for it anyway. */ 134 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 135 do { \ 136 long double _xpfpa_result = (val); \ 137 XPFPA_RESTORE(); \ 138 return _xpfpa_result; \ 139 } while (0) 140 141 #elif defined(HAVE__CONTROLFP) && !defined(__x86_64__) 142 143 /* float.h defines _controlfp */ 144 # include <float.h> 145 146 # define XPFPA_DECLARE \ 147 unsigned int _xpfpa_fpu_oldcw; 148 149 # define XPFPA_HAVE_CW 1 150 # define XPFPA_CW_DATATYPE \ 151 unsigned int 152 153 # define XPFPA_STORE_CW(vptr) do { \ 154 *((unsigned int *)(vptr)) = _controlfp(0, 0); \ 155 } while (0) 156 157 # define XPFPA_RESTORE_CW(vptr) do { \ 158 _controlfp(*((unsigned int *)(vptr)), _MCW_PC); \ 159 } while (0) 160 161 # define XPFPA_SWITCH_DOUBLE() do { \ 162 _xpfpa_fpu_oldcw = _controlfp(0, 0); \ 163 _controlfp(_PC_53, _MCW_PC); \ 164 } while (0) 165 # define XPFPA_SWITCH_SINGLE() do { \ 166 _xpfpa_fpu_oldcw = _controlfp(0, 0); \ 167 _controlfp(_PC_24, _MCW_PC); \ 168 } while (0) 169 /* NOTE: This will only work as expected on MinGW. */ 170 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 171 _xpfpa_fpu_oldcw = _controlfp(0, 0); \ 172 _controlfp(_PC_64, _MCW_PC); \ 173 } while (0) 174 # define XPFPA_RESTORE() \ 175 _controlfp(_xpfpa_fpu_oldcw, _MCW_PC) 176 /* We do NOT use the volatile return trick since _controlfp is a function 177 call and thus FP registers are saved in memory anyway. However, we do use 178 a variable to ensure that the expression passed into val will be evaluated 179 *before* switching back contexts. */ 180 # define XPFPA_RETURN_DOUBLE(val) \ 181 do { \ 182 double _xpfpa_result = (val); \ 183 XPFPA_RESTORE(); \ 184 return _xpfpa_result; \ 185 } while (0) 186 # define XPFPA_RETURN_SINGLE(val) \ 187 do { \ 188 float _xpfpa_result = (val); \ 189 XPFPA_RESTORE(); \ 190 return _xpfpa_result; \ 191 } while (0) 192 /* This will only work on MinGW */ 193 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 194 do { \ 195 long double _xpfpa_result = (val); \ 196 XPFPA_RESTORE(); \ 197 return _xpfpa_result; \ 198 } while (0) 199 200 #elif defined(HAVE__FPU_SETCW) && !defined(__x86_64__) /* glibc systems */ 201 202 /* fpu_control.h defines _FPU_[GS]ETCW */ 203 # include <fpu_control.h> 204 205 # define XPFPA_DECLARE \ 206 fpu_control_t _xpfpa_fpu_oldcw, _xpfpa_fpu_cw; 207 208 # define XPFPA_HAVE_CW 1 209 # define XPFPA_CW_DATATYPE \ 210 fpu_control_t 211 212 # define XPFPA_STORE_CW(vptr) do { \ 213 _FPU_GETCW((*((fpu_control_t *)(vptr)))); \ 214 } while (0) 215 216 # define XPFPA_RESTORE_CW(vptr) do { \ 217 _FPU_SETCW((*((fpu_control_t *)(vptr)))); \ 218 } while (0) 219 220 # define XPFPA_SWITCH_DOUBLE() do { \ 221 _FPU_GETCW(_xpfpa_fpu_oldcw); \ 222 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE; \ 223 _FPU_SETCW(_xpfpa_fpu_cw); \ 224 } while (0) 225 # define XPFPA_SWITCH_SINGLE() do { \ 226 _FPU_GETCW(_xpfpa_fpu_oldcw); \ 227 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_EXTENDED & ~_FPU_DOUBLE) | _FPU_SINGLE; \ 228 _FPU_SETCW(_xpfpa_fpu_cw); \ 229 } while (0) 230 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 231 _FPU_GETCW(_xpfpa_fpu_oldcw); \ 232 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~_FPU_SINGLE & ~_FPU_DOUBLE) | _FPU_EXTENDED; \ 233 _FPU_SETCW(_xpfpa_fpu_cw); \ 234 } while (0) 235 # define XPFPA_RESTORE() \ 236 _FPU_SETCW(_xpfpa_fpu_oldcw) 237 /* We use a temporary volatile variable (in a new block) in order to ensure 238 that the optimizer does not mis-optimize the instructions. Also, a volatile 239 variable ensures truncation to correct precision. */ 240 # define XPFPA_RETURN_DOUBLE(val) \ 241 do { \ 242 volatile double _xpfpa_result = (val); \ 243 XPFPA_RESTORE(); \ 244 return _xpfpa_result; \ 245 } while (0) 246 # define XPFPA_RETURN_SINGLE(val) \ 247 do { \ 248 volatile float _xpfpa_result = (val); \ 249 XPFPA_RESTORE(); \ 250 return _xpfpa_result; \ 251 } while (0) 252 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 253 do { \ 254 volatile long double _xpfpa_result = (val); \ 255 XPFPA_RESTORE(); \ 256 return _xpfpa_result; \ 257 } while (0) 258 259 #elif defined(HAVE_FPSETPREC) && !defined(__x86_64__) /* FreeBSD */ 260 261 /* fpu_control.h defines _FPU_[GS]ETCW */ 262 # include <machine/ieeefp.h> 263 264 # define XPFPA_DECLARE \ 265 fp_prec_t _xpfpa_fpu_oldprec; 266 267 # define XPFPA_HAVE_CW 1 268 # define XPFPA_CW_DATATYPE \ 269 fp_prec_t 270 271 # define XPFPA_STORE_CW(vptr) do { \ 272 *((fp_prec_t *)(vptr)) = fpgetprec(); \ 273 } while (0) 274 275 # define XPFPA_RESTORE_CW(vptr) do { \ 276 fpsetprec(*((fp_prec_t *)(vptr))); \ 277 } while (0) 278 279 # define XPFPA_SWITCH_DOUBLE() do { \ 280 _xpfpa_fpu_oldprec = fpgetprec(); \ 281 fpsetprec(FP_PD); \ 282 } while (0) 283 # define XPFPA_SWITCH_SINGLE() do { \ 284 _xpfpa_fpu_oldprec = fpgetprec(); \ 285 fpsetprec(FP_PS); \ 286 } while (0) 287 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 288 _xpfpa_fpu_oldprec = fpgetprec(); \ 289 fpsetprec(FP_PE); \ 290 } while (0) 291 # define XPFPA_RESTORE() \ 292 fpsetprec(_xpfpa_fpu_oldprec) 293 /* We use a temporary volatile variable (in a new block) in order to ensure 294 that the optimizer does not mis-optimize the instructions. Also, a volatile 295 variable ensures truncation to correct precision. */ 296 # define XPFPA_RETURN_DOUBLE(val) \ 297 do { \ 298 volatile double _xpfpa_result = (val); \ 299 XPFPA_RESTORE(); \ 300 return _xpfpa_result; \ 301 } while (0) 302 # define XPFPA_RETURN_SINGLE(val) \ 303 do { \ 304 volatile float _xpfpa_result = (val); \ 305 XPFPA_RESTORE(); \ 306 return _xpfpa_result; \ 307 } while (0) 308 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 309 do { \ 310 volatile long double _xpfpa_result = (val); \ 311 XPFPA_RESTORE(); \ 312 return _xpfpa_result; \ 313 } while (0) 314 315 #elif defined(HAVE_FPU_INLINE_ASM_X86) && !defined(__x86_64__) 316 317 /* 318 Custom x86 inline assembler implementation. 319 320 This implementation does not use predefined wrappers of the OS / compiler 321 but rather uses x86/x87 inline assembler directly. Basic instructions: 322 323 fnstcw - Store the FPU control word in a variable 324 fldcw - Load the FPU control word from a variable 325 326 Bits (only bits 8 and 9 are relevant, bits 0 to 7 are for other things): 327 0x0yy: Single precision 328 0x1yy: Reserved 329 0x2yy: Double precision 330 0x3yy: Double-extended precision 331 332 We use an unsigned int for the datatype. glibc sources add __mode__ (__HI__) 333 attribute to it (HI stands for half-integer according to docs). It is unclear 334 what the does exactly and how portable it is. 335 336 The assembly syntax works with GNU CC, Intel CC and Sun CC. 337 */ 338 339 # define XPFPA_DECLARE \ 340 unsigned int _xpfpa_fpu_oldcw, _xpfpa_fpu_cw; 341 342 # define XPFPA_HAVE_CW 1 343 # define XPFPA_CW_DATATYPE \ 344 unsigned int 345 346 # define XPFPA_STORE_CW(vptr) do { \ 347 __asm__ __volatile__ ("fnstcw %0" : "=m" (*((unsigned int *)(vptr)))); \ 348 } while (0) 349 350 # define XPFPA_RESTORE_CW(vptr) do { \ 351 __asm__ __volatile__ ("fldcw %0" : : "m" (*((unsigned int *)(vptr)))); \ 352 } while (0) 353 354 # define XPFPA_SWITCH_DOUBLE() do { \ 355 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \ 356 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x100) | 0x200; \ 357 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \ 358 } while (0) 359 # define XPFPA_SWITCH_SINGLE() do { \ 360 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \ 361 _xpfpa_fpu_cw = (_xpfpa_fpu_oldcw & ~0x300); \ 362 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \ 363 } while (0) 364 # define XPFPA_SWITCH_DOUBLE_EXTENDED() do { \ 365 __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_xpfpa_fpu_oldcw)); \ 366 _xpfpa_fpu_cw = _xpfpa_fpu_oldcw | 0x300; \ 367 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_cw)); \ 368 } while (0) 369 # define XPFPA_RESTORE() \ 370 __asm__ __volatile__ ("fldcw %0" : : "m" (*&_xpfpa_fpu_oldcw)) 371 /* We use a temporary volatile variable (in a new block) in order to ensure 372 that the optimizer does not mis-optimize the instructions. Also, a volatile 373 variable ensures truncation to correct precision. */ 374 # define XPFPA_RETURN_DOUBLE(val) \ 375 do { \ 376 volatile double _xpfpa_result = (val); \ 377 XPFPA_RESTORE(); \ 378 return _xpfpa_result; \ 379 } while (0) 380 # define XPFPA_RETURN_SINGLE(val) \ 381 do { \ 382 volatile float _xpfpa_result = (val); \ 383 XPFPA_RESTORE(); \ 384 return _xpfpa_result; \ 385 } while (0) 386 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) \ 387 do { \ 388 volatile long double _xpfpa_result = (val); \ 389 XPFPA_RESTORE(); \ 390 return _xpfpa_result; \ 391 } while (0) 392 393 #else /* FPU CONTROL */ 394 395 /* 396 This is either not an x87 FPU or the inline assembly syntax was not 397 recognized. In any case, default to NOPs for the macros and hope the 398 generated code will behave as planned. 399 */ 400 # define XPFPA_DECLARE /* NOP */ 401 # define XPFPA_HAVE_CW 0 402 # define XPFPA_CW_DATATYPE unsigned int 403 # define XPFPA_STORE_CW(variable) /* NOP */ 404 # define XPFPA_RESTORE_CW(variable) /* NOP */ 405 # define XPFPA_SWITCH_DOUBLE() /* NOP */ 406 # define XPFPA_SWITCH_SINGLE() /* NOP */ 407 # define XPFPA_SWITCH_DOUBLE_EXTENDED() /* NOP */ 408 # define XPFPA_RESTORE() /* NOP */ 409 # define XPFPA_RETURN_DOUBLE(val) return (val) 410 # define XPFPA_RETURN_SINGLE(val) return (val) 411 # define XPFPA_RETURN_DOUBLE_EXTENDED(val) return (val) 412 413 #endif /* FPU CONTROL */ 414 415 #endif 416