xref: /PHP-7.2/Zend/zend_float.h (revision 7a7ec01a)
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