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