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