1 /* IA-32 floating point unit inline related functions.
2    Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
3    Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
4 
5 This file is part of the Parma Polyhedra Library (PPL).
6 
7 The PPL is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 The PPL is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
20 
21 For the most up-to-date information see the Parma Polyhedra Library
22 site: http://bugseng.com/products/ppl/ . */
23 
24 #ifndef PPL_fpu_ia32_inlines_hh
25 #define PPL_fpu_ia32_inlines_hh 1
26 
27 #include <csetjmp>
28 #include <csignal>
29 
30 #define FPU_INVALID       0x01
31 #define FPU_DIVBYZERO     0x04
32 #define FPU_OVERFLOW      0x08
33 #define FPU_UNDERFLOW     0x10
34 #define FPU_INEXACT       0x20
35 
36 #define FPU_ALL_EXCEPT \
37   (FPU_INEXACT | FPU_DIVBYZERO | FPU_UNDERFLOW | FPU_OVERFLOW | FPU_INVALID)
38 
39 #define PPL_FPU_TONEAREST     0
40 #define PPL_FPU_DOWNWARD      0x400
41 #define PPL_FPU_UPWARD        0x800
42 #define PPL_FPU_TOWARDZERO    0xc00
43 
44 #define FPU_ROUNDING_MASK 0xc00
45 
46 #define SSE_INEXACT       0x20
47 
48 #define PPL_FPU_CONTROL_DEFAULT_BASE 0x37f
49 #define PPL_SSE_CONTROL_DEFAULT_BASE 0x1f80
50 
51 // This MUST be congruent with the definition of ROUND_DIRECT
52 #define PPL_FPU_CONTROL_DEFAULT \
53   (PPL_FPU_CONTROL_DEFAULT_BASE | PPL_FPU_UPWARD)
54 #define PPL_SSE_CONTROL_DEFAULT \
55   (PPL_SSE_CONTROL_DEFAULT_BASE | (PPL_FPU_UPWARD << 3))
56 
57 namespace Parma_Polyhedra_Library {
58 
59 typedef struct {
60   unsigned short control_word;
61   unsigned short unused1;
62   unsigned short status_word;
63   unsigned short unused2;
64   unsigned short tags;
65   unsigned short unused3;
66   unsigned int eip;
67   unsigned short cs_selector;
68   unsigned int opcode:11;
69   unsigned int unused4:5;
70   unsigned int data_offset;
71   unsigned short data_selector;
72   unsigned short unused5;
73 } ia32_fenv_t;
74 
75 inline int
fpu_get_control()76 fpu_get_control() {
77   unsigned short cw;
78   __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw) : : "memory");
79   return cw;
80 }
81 
82 inline void
fpu_set_control(int c)83 fpu_set_control(int c) {
84   unsigned short cw = static_cast<unsigned short>(c);
85   __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw) : "memory");
86 }
87 
88 inline int
fpu_get_status()89 fpu_get_status() {
90   unsigned short sw;
91   __asm__ __volatile__ ("fnstsw %0" : "=a" (sw) : : "memory");
92   return sw;
93 }
94 
95 inline void
fpu_clear_status(unsigned short bits)96 fpu_clear_status(unsigned short bits) {
97   /* There is no fldsw instruction */
98   ia32_fenv_t env;
99   __asm__ __volatile__ ("fnstenv %0" : "=m" (env));
100   env.status_word = static_cast<unsigned short>(env.status_word & ~bits);
101   __asm__ __volatile__ ("fldenv %0" : : "m" (env) : "memory");
102 }
103 
104 inline void
fpu_clear_exceptions()105 fpu_clear_exceptions() {
106   __asm__ __volatile__ ("fnclex" : /* No outputs.  */ : : "memory");
107 }
108 
109 #ifdef PPL_FPMATH_MAY_USE_SSE
110 inline void
sse_set_control(unsigned int cw)111 sse_set_control(unsigned int cw) {
112   __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&cw) : "memory");
113 }
114 
115 inline unsigned int
sse_get_control()116 sse_get_control() {
117   unsigned int cw;
118   __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&cw) : : "memory");
119   return cw;
120 }
121 #endif
122 
123 inline void
fpu_initialize_control_functions()124 fpu_initialize_control_functions() {
125 #ifdef PPL_FPMATH_MAY_USE_SSE
126   extern void detect_sse_unit();
127   detect_sse_unit();
128 #endif
129 }
130 
131 inline fpu_rounding_direction_type
fpu_get_rounding_direction()132 fpu_get_rounding_direction() {
133   return static_cast<fpu_rounding_direction_type>(fpu_get_control() & FPU_ROUNDING_MASK);
134 }
135 
136 inline void
fpu_set_rounding_direction(fpu_rounding_direction_type dir)137 fpu_set_rounding_direction(fpu_rounding_direction_type dir) {
138 #ifdef PPL_FPMATH_MAY_USE_387
139   fpu_set_control(PPL_FPU_CONTROL_DEFAULT_BASE | dir);
140 #endif
141 #ifdef PPL_FPMATH_MAY_USE_SSE
142   extern bool have_sse_unit;
143   if (have_sse_unit)
144     sse_set_control(PPL_SSE_CONTROL_DEFAULT_BASE | (dir << 3));
145 #endif
146 }
147 
148 inline fpu_rounding_control_word_type
fpu_save_rounding_direction(fpu_rounding_direction_type dir)149 fpu_save_rounding_direction(fpu_rounding_direction_type dir) {
150 #ifdef PPL_FPMATH_MAY_USE_387
151   fpu_set_control(PPL_FPU_CONTROL_DEFAULT_BASE | dir);
152 #endif
153 #ifdef PPL_FPMATH_MAY_USE_SSE
154   extern bool have_sse_unit;
155   if (have_sse_unit)
156     sse_set_control(PPL_SSE_CONTROL_DEFAULT_BASE | (dir << 3));
157 #endif
158   return static_cast<fpu_rounding_control_word_type>(0);
159 }
160 
161 inline void
fpu_reset_inexact()162 fpu_reset_inexact() {
163 #ifdef PPL_FPMATH_MAY_USE_387
164   fpu_clear_exceptions();
165 #endif
166 #ifdef PPL_FPMATH_MAY_USE_SSE
167   // NOTE: on entry to this function the current rounding mode
168   // has to be the default one.
169   extern bool have_sse_unit;
170   if (have_sse_unit) {
171     sse_set_control(PPL_SSE_CONTROL_DEFAULT);
172   }
173 #endif
174 }
175 
176 inline void
fpu_restore_rounding_direction(fpu_rounding_control_word_type)177 fpu_restore_rounding_direction(fpu_rounding_control_word_type) {
178 #ifdef PPL_FPMATH_MAY_USE_387
179   fpu_set_control(PPL_FPU_CONTROL_DEFAULT);
180 #endif
181 #ifdef PPL_FPMATH_MAY_USE_SSE
182   extern bool have_sse_unit;
183   if (have_sse_unit) {
184     sse_set_control(PPL_SSE_CONTROL_DEFAULT);
185   }
186 #endif
187 }
188 
189 inline int
fpu_check_inexact()190 fpu_check_inexact() {
191 #ifdef PPL_FPMATH_MAY_USE_387
192   if (fpu_get_status() & FPU_INEXACT) {
193     return 1;
194   }
195 #endif
196 #ifdef PPL_FPMATH_MAY_USE_SSE
197   extern bool have_sse_unit;
198   if (have_sse_unit && (sse_get_control() & SSE_INEXACT)) {
199     return 1;
200   }
201 #endif
202   return 0;
203 }
204 
205 } // namespace Parma_Polyhedra_Library
206 
207 #endif // !defined(PPL_fpu_ia32_inlines_hh)
208