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