fpu-387.h revision 1.1.1.2 1 1.1 mrg /* FPU-related code for x86 and x86_64 processors.
2 1.1.1.2 mrg Copyright (C) 2005-2020 Free Software Foundation, Inc.
3 1.1 mrg Contributed by Francois-Xavier Coudert <coudert (at) clipper.ens.fr>
4 1.1 mrg
5 1.1 mrg This file is part of the GNU Fortran 95 runtime library (libgfortran).
6 1.1 mrg
7 1.1 mrg Libgfortran is free software; you can redistribute it and/or
8 1.1 mrg modify it under the terms of the GNU General Public
9 1.1 mrg License as published by the Free Software Foundation; either
10 1.1 mrg version 3 of the License, or (at your option) any later version.
11 1.1 mrg
12 1.1 mrg Libgfortran is distributed in the hope that it will be useful,
13 1.1 mrg but WITHOUT ANY WARRANTY; without even the implied warranty of
14 1.1 mrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 1.1 mrg GNU General Public License for more details.
16 1.1 mrg
17 1.1 mrg Under Section 7 of GPL version 3, you are granted additional
18 1.1 mrg permissions described in the GCC Runtime Library Exception, version
19 1.1 mrg 3.1, as published by the Free Software Foundation.
20 1.1 mrg
21 1.1 mrg You should have received a copy of the GNU General Public License and
22 1.1 mrg a copy of the GCC Runtime Library Exception along with this program;
23 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 1.1 mrg <http://www.gnu.org/licenses/>. */
25 1.1 mrg
26 1.1 mrg #ifndef __SSE_MATH__
27 1.1 mrg #include "cpuid.h"
28 1.1 mrg #endif
29 1.1 mrg
30 1.1 mrg static int
31 1.1 mrg has_sse (void)
32 1.1 mrg {
33 1.1 mrg #ifndef __SSE_MATH__
34 1.1 mrg unsigned int eax, ebx, ecx, edx;
35 1.1 mrg
36 1.1 mrg if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx))
37 1.1 mrg return 0;
38 1.1 mrg
39 1.1 mrg return edx & bit_SSE;
40 1.1 mrg #else
41 1.1 mrg return 1;
42 1.1 mrg #endif
43 1.1 mrg }
44 1.1 mrg
45 1.1 mrg /* i387 exceptions -- see linux <fpu_control.h> header file for details. */
46 1.1 mrg #define _FPU_MASK_IM 0x01
47 1.1 mrg #define _FPU_MASK_DM 0x02
48 1.1 mrg #define _FPU_MASK_ZM 0x04
49 1.1 mrg #define _FPU_MASK_OM 0x08
50 1.1 mrg #define _FPU_MASK_UM 0x10
51 1.1 mrg #define _FPU_MASK_PM 0x20
52 1.1 mrg #define _FPU_MASK_ALL 0x3f
53 1.1 mrg
54 1.1 mrg #define _FPU_EX_ALL 0x3f
55 1.1 mrg
56 1.1 mrg /* i387 rounding modes. */
57 1.1 mrg
58 1.1 mrg #define _FPU_RC_NEAREST 0x0
59 1.1 mrg #define _FPU_RC_DOWN 0x1
60 1.1 mrg #define _FPU_RC_UP 0x2
61 1.1 mrg #define _FPU_RC_ZERO 0x3
62 1.1 mrg
63 1.1 mrg #define _FPU_RC_MASK 0x3
64 1.1 mrg
65 1.1 mrg /* Enable flush to zero mode. */
66 1.1 mrg
67 1.1 mrg #define MXCSR_FTZ (1 << 15)
68 1.1 mrg
69 1.1 mrg
70 1.1 mrg /* This structure corresponds to the layout of the block
71 1.1 mrg written by FSTENV. */
72 1.1 mrg typedef struct
73 1.1 mrg {
74 1.1 mrg unsigned short int __control_word;
75 1.1 mrg unsigned short int __unused1;
76 1.1 mrg unsigned short int __status_word;
77 1.1 mrg unsigned short int __unused2;
78 1.1 mrg unsigned short int __tags;
79 1.1 mrg unsigned short int __unused3;
80 1.1 mrg unsigned int __eip;
81 1.1 mrg unsigned short int __cs_selector;
82 1.1 mrg unsigned short int __opcode;
83 1.1 mrg unsigned int __data_offset;
84 1.1 mrg unsigned short int __data_selector;
85 1.1 mrg unsigned short int __unused5;
86 1.1 mrg unsigned int __mxcsr;
87 1.1 mrg }
88 1.1 mrg my_fenv_t;
89 1.1 mrg
90 1.1 mrg /* Check we can actually store the FPU state in the allocated size. */
91 1.1 mrg _Static_assert (sizeof(my_fenv_t) <= (size_t) GFC_FPE_STATE_BUFFER_SIZE,
92 1.1 mrg "GFC_FPE_STATE_BUFFER_SIZE is too small");
93 1.1 mrg
94 1.1 mrg
95 1.1 mrg /* Raise the supported floating-point exceptions from EXCEPTS. Other
96 1.1 mrg bits in EXCEPTS are ignored. Code originally borrowed from
97 1.1 mrg libatomic/config/x86/fenv.c. */
98 1.1 mrg
99 1.1 mrg static void
100 1.1 mrg local_feraiseexcept (int excepts)
101 1.1 mrg {
102 1.1 mrg if (excepts & _FPU_MASK_IM)
103 1.1 mrg {
104 1.1 mrg float f = 0.0f;
105 1.1 mrg #ifdef __SSE_MATH__
106 1.1 mrg __asm__ __volatile__ ("%vdivss\t{%0, %d0|%d0, %0}" : "+x" (f));
107 1.1 mrg #else
108 1.1 mrg __asm__ __volatile__ ("fdiv\t{%y0, %0|%0, %y0}" : "+t" (f));
109 1.1 mrg /* No need for fwait, exception is triggered by emitted fstp. */
110 1.1 mrg #endif
111 1.1 mrg }
112 1.1 mrg if (excepts & _FPU_MASK_DM)
113 1.1 mrg {
114 1.1 mrg my_fenv_t temp;
115 1.1 mrg __asm__ __volatile__ ("fnstenv\t%0" : "=m" (temp));
116 1.1 mrg temp.__status_word |= _FPU_MASK_DM;
117 1.1 mrg __asm__ __volatile__ ("fldenv\t%0" : : "m" (temp));
118 1.1 mrg __asm__ __volatile__ ("fwait");
119 1.1 mrg }
120 1.1 mrg if (excepts & _FPU_MASK_ZM)
121 1.1 mrg {
122 1.1 mrg float f = 1.0f, g = 0.0f;
123 1.1 mrg #ifdef __SSE_MATH__
124 1.1 mrg __asm__ __volatile__ ("%vdivss\t{%1, %d0|%d0, %1}" : "+x" (f) : "xm" (g));
125 1.1 mrg #else
126 1.1 mrg __asm__ __volatile__ ("fdivs\t%1" : "+t" (f) : "m" (g));
127 1.1 mrg /* No need for fwait, exception is triggered by emitted fstp. */
128 1.1 mrg #endif
129 1.1 mrg }
130 1.1 mrg if (excepts & _FPU_MASK_OM)
131 1.1 mrg {
132 1.1 mrg my_fenv_t temp;
133 1.1 mrg __asm__ __volatile__ ("fnstenv\t%0" : "=m" (temp));
134 1.1 mrg temp.__status_word |= _FPU_MASK_OM;
135 1.1 mrg __asm__ __volatile__ ("fldenv\t%0" : : "m" (temp));
136 1.1 mrg __asm__ __volatile__ ("fwait");
137 1.1 mrg }
138 1.1 mrg if (excepts & _FPU_MASK_UM)
139 1.1 mrg {
140 1.1 mrg my_fenv_t temp;
141 1.1 mrg __asm__ __volatile__ ("fnstenv\t%0" : "=m" (temp));
142 1.1 mrg temp.__status_word |= _FPU_MASK_UM;
143 1.1 mrg __asm__ __volatile__ ("fldenv\t%0" : : "m" (temp));
144 1.1 mrg __asm__ __volatile__ ("fwait");
145 1.1 mrg }
146 1.1 mrg if (excepts & _FPU_MASK_PM)
147 1.1 mrg {
148 1.1 mrg float f = 1.0f, g = 3.0f;
149 1.1 mrg #ifdef __SSE_MATH__
150 1.1 mrg __asm__ __volatile__ ("%vdivss\t{%1, %d0|%d0, %1}" : "+x" (f) : "xm" (g));
151 1.1 mrg #else
152 1.1 mrg __asm__ __volatile__ ("fdivs\t%1" : "+t" (f) : "m" (g));
153 1.1 mrg /* No need for fwait, exception is triggered by emitted fstp. */
154 1.1 mrg #endif
155 1.1 mrg }
156 1.1 mrg }
157 1.1 mrg
158 1.1 mrg
159 1.1 mrg void
160 1.1 mrg set_fpu_trap_exceptions (int trap, int notrap)
161 1.1 mrg {
162 1.1 mrg int exc_set = 0, exc_clr = 0;
163 1.1 mrg unsigned short cw;
164 1.1 mrg
165 1.1 mrg if (trap & GFC_FPE_INVALID) exc_set |= _FPU_MASK_IM;
166 1.1 mrg if (trap & GFC_FPE_DENORMAL) exc_set |= _FPU_MASK_DM;
167 1.1 mrg if (trap & GFC_FPE_ZERO) exc_set |= _FPU_MASK_ZM;
168 1.1 mrg if (trap & GFC_FPE_OVERFLOW) exc_set |= _FPU_MASK_OM;
169 1.1 mrg if (trap & GFC_FPE_UNDERFLOW) exc_set |= _FPU_MASK_UM;
170 1.1 mrg if (trap & GFC_FPE_INEXACT) exc_set |= _FPU_MASK_PM;
171 1.1 mrg
172 1.1 mrg if (notrap & GFC_FPE_INVALID) exc_clr |= _FPU_MASK_IM;
173 1.1 mrg if (notrap & GFC_FPE_DENORMAL) exc_clr |= _FPU_MASK_DM;
174 1.1 mrg if (notrap & GFC_FPE_ZERO) exc_clr |= _FPU_MASK_ZM;
175 1.1 mrg if (notrap & GFC_FPE_OVERFLOW) exc_clr |= _FPU_MASK_OM;
176 1.1 mrg if (notrap & GFC_FPE_UNDERFLOW) exc_clr |= _FPU_MASK_UM;
177 1.1 mrg if (notrap & GFC_FPE_INEXACT) exc_clr |= _FPU_MASK_PM;
178 1.1 mrg
179 1.1 mrg __asm__ __volatile__ ("fstcw\t%0" : "=m" (cw));
180 1.1 mrg
181 1.1 mrg cw |= exc_clr;
182 1.1 mrg cw &= ~exc_set;
183 1.1 mrg
184 1.1 mrg __asm__ __volatile__ ("fnclex\n\tfldcw\t%0" : : "m" (cw));
185 1.1 mrg
186 1.1 mrg if (has_sse())
187 1.1 mrg {
188 1.1 mrg unsigned int cw_sse;
189 1.1 mrg
190 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
191 1.1 mrg
192 1.1 mrg /* The SSE exception masks are shifted by 7 bits. */
193 1.1 mrg cw_sse |= (exc_clr << 7);
194 1.1 mrg cw_sse &= ~(exc_set << 7);
195 1.1 mrg
196 1.1 mrg /* Clear stalled exception flags. */
197 1.1 mrg cw_sse &= ~_FPU_EX_ALL;
198 1.1 mrg
199 1.1 mrg __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (cw_sse));
200 1.1 mrg }
201 1.1 mrg }
202 1.1 mrg
203 1.1 mrg void
204 1.1 mrg set_fpu (void)
205 1.1 mrg {
206 1.1 mrg set_fpu_trap_exceptions (options.fpe, 0);
207 1.1 mrg }
208 1.1 mrg
209 1.1 mrg int
210 1.1 mrg get_fpu_trap_exceptions (void)
211 1.1 mrg {
212 1.1 mrg unsigned short cw;
213 1.1 mrg int mask;
214 1.1 mrg int res = 0;
215 1.1 mrg
216 1.1 mrg __asm__ __volatile__ ("fstcw\t%0" : "=m" (cw));
217 1.1 mrg mask = cw;
218 1.1 mrg
219 1.1 mrg if (has_sse())
220 1.1 mrg {
221 1.1 mrg unsigned int cw_sse;
222 1.1 mrg
223 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
224 1.1 mrg
225 1.1 mrg /* The SSE exception masks are shifted by 7 bits. */
226 1.1 mrg mask |= (cw_sse >> 7);
227 1.1 mrg }
228 1.1 mrg
229 1.1 mrg mask = ~mask & _FPU_MASK_ALL;
230 1.1 mrg
231 1.1 mrg if (mask & _FPU_MASK_IM) res |= GFC_FPE_INVALID;
232 1.1 mrg if (mask & _FPU_MASK_DM) res |= GFC_FPE_DENORMAL;
233 1.1 mrg if (mask & _FPU_MASK_ZM) res |= GFC_FPE_ZERO;
234 1.1 mrg if (mask & _FPU_MASK_OM) res |= GFC_FPE_OVERFLOW;
235 1.1 mrg if (mask & _FPU_MASK_UM) res |= GFC_FPE_UNDERFLOW;
236 1.1 mrg if (mask & _FPU_MASK_PM) res |= GFC_FPE_INEXACT;
237 1.1 mrg
238 1.1 mrg return res;
239 1.1 mrg }
240 1.1 mrg
241 1.1 mrg int
242 1.1 mrg support_fpu_trap (int flag __attribute__((unused)))
243 1.1 mrg {
244 1.1 mrg return 1;
245 1.1 mrg }
246 1.1 mrg
247 1.1 mrg int
248 1.1 mrg get_fpu_except_flags (void)
249 1.1 mrg {
250 1.1 mrg unsigned short cw;
251 1.1 mrg int excepts;
252 1.1 mrg int res = 0;
253 1.1 mrg
254 1.1 mrg __asm__ __volatile__ ("fnstsw\t%0" : "=am" (cw));
255 1.1 mrg excepts = cw;
256 1.1 mrg
257 1.1 mrg if (has_sse())
258 1.1 mrg {
259 1.1 mrg unsigned int cw_sse;
260 1.1 mrg
261 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
262 1.1 mrg excepts |= cw_sse;
263 1.1 mrg }
264 1.1 mrg
265 1.1 mrg excepts &= _FPU_EX_ALL;
266 1.1 mrg
267 1.1 mrg if (excepts & _FPU_MASK_IM) res |= GFC_FPE_INVALID;
268 1.1 mrg if (excepts & _FPU_MASK_DM) res |= GFC_FPE_DENORMAL;
269 1.1 mrg if (excepts & _FPU_MASK_ZM) res |= GFC_FPE_ZERO;
270 1.1 mrg if (excepts & _FPU_MASK_OM) res |= GFC_FPE_OVERFLOW;
271 1.1 mrg if (excepts & _FPU_MASK_UM) res |= GFC_FPE_UNDERFLOW;
272 1.1 mrg if (excepts & _FPU_MASK_PM) res |= GFC_FPE_INEXACT;
273 1.1 mrg
274 1.1 mrg return res;
275 1.1 mrg }
276 1.1 mrg
277 1.1 mrg void
278 1.1 mrg set_fpu_except_flags (int set, int clear)
279 1.1 mrg {
280 1.1 mrg my_fenv_t temp;
281 1.1 mrg int exc_set = 0, exc_clr = 0;
282 1.1 mrg
283 1.1 mrg /* Translate from GFC_PE_* values to _FPU_MASK_* values. */
284 1.1 mrg if (set & GFC_FPE_INVALID)
285 1.1 mrg exc_set |= _FPU_MASK_IM;
286 1.1 mrg if (clear & GFC_FPE_INVALID)
287 1.1 mrg exc_clr |= _FPU_MASK_IM;
288 1.1 mrg
289 1.1 mrg if (set & GFC_FPE_DENORMAL)
290 1.1 mrg exc_set |= _FPU_MASK_DM;
291 1.1 mrg if (clear & GFC_FPE_DENORMAL)
292 1.1 mrg exc_clr |= _FPU_MASK_DM;
293 1.1 mrg
294 1.1 mrg if (set & GFC_FPE_ZERO)
295 1.1 mrg exc_set |= _FPU_MASK_ZM;
296 1.1 mrg if (clear & GFC_FPE_ZERO)
297 1.1 mrg exc_clr |= _FPU_MASK_ZM;
298 1.1 mrg
299 1.1 mrg if (set & GFC_FPE_OVERFLOW)
300 1.1 mrg exc_set |= _FPU_MASK_OM;
301 1.1 mrg if (clear & GFC_FPE_OVERFLOW)
302 1.1 mrg exc_clr |= _FPU_MASK_OM;
303 1.1 mrg
304 1.1 mrg if (set & GFC_FPE_UNDERFLOW)
305 1.1 mrg exc_set |= _FPU_MASK_UM;
306 1.1 mrg if (clear & GFC_FPE_UNDERFLOW)
307 1.1 mrg exc_clr |= _FPU_MASK_UM;
308 1.1 mrg
309 1.1 mrg if (set & GFC_FPE_INEXACT)
310 1.1 mrg exc_set |= _FPU_MASK_PM;
311 1.1 mrg if (clear & GFC_FPE_INEXACT)
312 1.1 mrg exc_clr |= _FPU_MASK_PM;
313 1.1 mrg
314 1.1 mrg
315 1.1 mrg /* Change the flags. This is tricky on 387 (unlike SSE), because we have
316 1.1 mrg FNSTSW but no FLDSW instruction. */
317 1.1 mrg __asm__ __volatile__ ("fnstenv\t%0" : "=m" (temp));
318 1.1 mrg temp.__status_word &= ~exc_clr;
319 1.1 mrg __asm__ __volatile__ ("fldenv\t%0" : : "m" (temp));
320 1.1 mrg
321 1.1 mrg /* Change the flags on SSE. */
322 1.1 mrg
323 1.1 mrg if (has_sse())
324 1.1 mrg {
325 1.1 mrg unsigned int cw_sse;
326 1.1 mrg
327 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
328 1.1 mrg cw_sse &= ~exc_clr;
329 1.1 mrg __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (cw_sse));
330 1.1 mrg }
331 1.1 mrg
332 1.1 mrg local_feraiseexcept (exc_set);
333 1.1 mrg }
334 1.1 mrg
335 1.1 mrg int
336 1.1 mrg support_fpu_flag (int flag __attribute__((unused)))
337 1.1 mrg {
338 1.1 mrg return 1;
339 1.1 mrg }
340 1.1 mrg
341 1.1 mrg void
342 1.1 mrg set_fpu_rounding_mode (int round)
343 1.1 mrg {
344 1.1 mrg int round_mode;
345 1.1 mrg unsigned short cw;
346 1.1 mrg
347 1.1 mrg switch (round)
348 1.1 mrg {
349 1.1 mrg case GFC_FPE_TONEAREST:
350 1.1 mrg round_mode = _FPU_RC_NEAREST;
351 1.1 mrg break;
352 1.1 mrg case GFC_FPE_UPWARD:
353 1.1 mrg round_mode = _FPU_RC_UP;
354 1.1 mrg break;
355 1.1 mrg case GFC_FPE_DOWNWARD:
356 1.1 mrg round_mode = _FPU_RC_DOWN;
357 1.1 mrg break;
358 1.1 mrg case GFC_FPE_TOWARDZERO:
359 1.1 mrg round_mode = _FPU_RC_ZERO;
360 1.1 mrg break;
361 1.1 mrg default:
362 1.1 mrg return; /* Should be unreachable. */
363 1.1 mrg }
364 1.1 mrg
365 1.1 mrg __asm__ __volatile__ ("fnstcw\t%0" : "=m" (cw));
366 1.1 mrg
367 1.1 mrg /* The x87 round control bits are shifted by 10 bits. */
368 1.1 mrg cw &= ~(_FPU_RC_MASK << 10);
369 1.1 mrg cw |= round_mode << 10;
370 1.1 mrg
371 1.1 mrg __asm__ __volatile__ ("fldcw\t%0" : : "m" (cw));
372 1.1 mrg
373 1.1 mrg if (has_sse())
374 1.1 mrg {
375 1.1 mrg unsigned int cw_sse;
376 1.1 mrg
377 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
378 1.1 mrg
379 1.1 mrg /* The SSE round control bits are shifted by 13 bits. */
380 1.1 mrg cw_sse &= ~(_FPU_RC_MASK << 13);
381 1.1 mrg cw_sse |= round_mode << 13;
382 1.1 mrg
383 1.1 mrg __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (cw_sse));
384 1.1 mrg }
385 1.1 mrg }
386 1.1 mrg
387 1.1 mrg int
388 1.1 mrg get_fpu_rounding_mode (void)
389 1.1 mrg {
390 1.1 mrg int round_mode;
391 1.1 mrg
392 1.1 mrg #ifdef __SSE_MATH__
393 1.1 mrg unsigned int cw;
394 1.1 mrg
395 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw));
396 1.1 mrg
397 1.1 mrg /* The SSE round control bits are shifted by 13 bits. */
398 1.1 mrg round_mode = cw >> 13;
399 1.1 mrg #else
400 1.1 mrg unsigned short cw;
401 1.1 mrg
402 1.1 mrg __asm__ __volatile__ ("fnstcw\t%0" : "=m" (cw));
403 1.1 mrg
404 1.1 mrg /* The x87 round control bits are shifted by 10 bits. */
405 1.1 mrg round_mode = cw >> 10;
406 1.1 mrg #endif
407 1.1 mrg
408 1.1 mrg round_mode &= _FPU_RC_MASK;
409 1.1 mrg
410 1.1 mrg switch (round_mode)
411 1.1 mrg {
412 1.1 mrg case _FPU_RC_NEAREST:
413 1.1 mrg return GFC_FPE_TONEAREST;
414 1.1 mrg case _FPU_RC_UP:
415 1.1 mrg return GFC_FPE_UPWARD;
416 1.1 mrg case _FPU_RC_DOWN:
417 1.1 mrg return GFC_FPE_DOWNWARD;
418 1.1 mrg case _FPU_RC_ZERO:
419 1.1 mrg return GFC_FPE_TOWARDZERO;
420 1.1 mrg default:
421 1.1 mrg return 0; /* Should be unreachable. */
422 1.1 mrg }
423 1.1 mrg }
424 1.1 mrg
425 1.1 mrg int
426 1.1 mrg support_fpu_rounding_mode (int mode __attribute__((unused)))
427 1.1 mrg {
428 1.1 mrg return 1;
429 1.1 mrg }
430 1.1 mrg
431 1.1 mrg void
432 1.1 mrg get_fpu_state (void *state)
433 1.1 mrg {
434 1.1 mrg my_fenv_t *envp = state;
435 1.1 mrg
436 1.1 mrg __asm__ __volatile__ ("fnstenv\t%0" : "=m" (*envp));
437 1.1 mrg
438 1.1 mrg /* fnstenv has the side effect of masking all exceptions, so we need
439 1.1 mrg to restore the control word after that. */
440 1.1 mrg __asm__ __volatile__ ("fldcw\t%0" : : "m" (envp->__control_word));
441 1.1 mrg
442 1.1 mrg if (has_sse())
443 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (envp->__mxcsr));
444 1.1 mrg }
445 1.1 mrg
446 1.1 mrg void
447 1.1 mrg set_fpu_state (void *state)
448 1.1 mrg {
449 1.1 mrg my_fenv_t *envp = state;
450 1.1 mrg
451 1.1 mrg /* glibc sources (sysdeps/x86_64/fpu/fesetenv.c) do something more
452 1.1 mrg complex than this, but I think it suffices in our case. */
453 1.1 mrg __asm__ __volatile__ ("fldenv\t%0" : : "m" (*envp));
454 1.1 mrg
455 1.1 mrg if (has_sse())
456 1.1 mrg __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (envp->__mxcsr));
457 1.1 mrg }
458 1.1 mrg
459 1.1 mrg
460 1.1 mrg int
461 1.1 mrg support_fpu_underflow_control (int kind)
462 1.1 mrg {
463 1.1 mrg if (!has_sse())
464 1.1 mrg return 0;
465 1.1 mrg
466 1.1 mrg return (kind == 4 || kind == 8) ? 1 : 0;
467 1.1 mrg }
468 1.1 mrg
469 1.1 mrg
470 1.1 mrg int
471 1.1 mrg get_fpu_underflow_mode (void)
472 1.1 mrg {
473 1.1 mrg unsigned int cw_sse;
474 1.1 mrg
475 1.1 mrg if (!has_sse())
476 1.1 mrg return 1;
477 1.1 mrg
478 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
479 1.1 mrg
480 1.1 mrg /* Return 0 for abrupt underflow (flush to zero), 1 for gradual underflow. */
481 1.1 mrg return (cw_sse & MXCSR_FTZ) ? 0 : 1;
482 1.1 mrg }
483 1.1 mrg
484 1.1 mrg
485 1.1 mrg void
486 1.1 mrg set_fpu_underflow_mode (int gradual)
487 1.1 mrg {
488 1.1 mrg unsigned int cw_sse;
489 1.1 mrg
490 1.1 mrg if (!has_sse())
491 1.1 mrg return;
492 1.1 mrg
493 1.1 mrg __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
494 1.1 mrg
495 1.1 mrg if (gradual)
496 1.1 mrg cw_sse &= ~MXCSR_FTZ;
497 1.1 mrg else
498 1.1 mrg cw_sse |= MXCSR_FTZ;
499 1.1 mrg
500 1.1 mrg __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (cw_sse));
501 1.1 mrg }
502 1.1 mrg
503