1: /*
2: +----------------------------------------------------------------------+
3: | Zend Engine |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1998-2014 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: zend_float.h,v 1.1.1.4 2014/06/15 20:04:03 misho Exp $ */
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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>