1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2001-2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "sys.h"
26 #include "global.h"
27 #include "erl_process.h"
28
29
30 #ifdef NO_FPE_SIGNALS
31
32 void
erts_sys_init_float(void)33 erts_sys_init_float(void)
34 {
35 # ifdef SIGFPE
36 sys_signal(SIGFPE, SIG_IGN); /* Ignore so we can test for NaN and Inf */
37 # endif
38 }
39
40 #else /* !NO_FPE_SIGNALS */
41
42 static erts_tsd_key_t fpe_key;
43
44 /* once-only initialisation early in the main thread (via erts_sys_init_float()) */
erts_init_fp_exception(void)45 static void erts_init_fp_exception(void)
46 {
47 /* XXX: the wrappers prevent using a pthread destructor to
48 deallocate the key's value; so when/where do we do that? */
49 erts_tsd_key_create(&fpe_key,"fp_exception");
50 }
51
erts_thread_init_fp_exception(void)52 void erts_thread_init_fp_exception(void)
53 {
54 unsigned long *fpe = erts_alloc(ERTS_ALC_T_FP_EXCEPTION, sizeof(*fpe));
55 *fpe = 0;
56 erts_tsd_set(fpe_key, fpe);
57 }
58
erts_thread_get_fp_exception(void)59 static ERTS_INLINE volatile unsigned long *erts_thread_get_fp_exception(void)
60 {
61 return (volatile unsigned long*)erts_tsd_get(fpe_key);
62 }
63
erts_get_current_fp_exception(void)64 volatile unsigned long *erts_get_current_fp_exception(void)
65 {
66 Process *c_p;
67
68 c_p = erts_get_current_process();
69 if (c_p)
70 return &c_p->fp_exception;
71 return erts_thread_get_fp_exception();
72 }
73
set_current_fp_exception(unsigned long pc)74 static void set_current_fp_exception(unsigned long pc)
75 {
76 volatile unsigned long *fpexnp = erts_get_current_fp_exception();
77 ASSERT(fpexnp != NULL);
78 *fpexnp = pc;
79 }
80
erts_fp_check_init_error(volatile unsigned long * fpexnp)81 void erts_fp_check_init_error(volatile unsigned long *fpexnp)
82 {
83 char buf[128];
84 snprintf(buf, sizeof buf, "ERTS_FP_CHECK_INIT at %p: detected unhandled FPE at %p\r\n",
85 __builtin_return_address(0), (void*)*fpexnp);
86 if (write(2, buf, strlen(buf)) <= 0)
87 erts_exit(ERTS_ABORT_EXIT, "%s", buf);
88 *fpexnp = 0;
89 #if defined(__i386__) || defined(__x86_64__)
90 erts_restore_fpu();
91 #endif
92 }
93
94 /* Is there no standard identifier for Darwin/MacOSX ? */
95 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
96 #define __DARWIN__ 1
97 #endif
98
99 /*
100 * Define two processor and possibly OS-specific primitives:
101 *
102 * static void unmask_fpe(void);
103 * -- unmask invalid, overflow, and divide-by-zero exceptions
104 *
105 * static int mask_fpe(void);
106 * -- mask invalid, overflow, and divide-by-zero exceptions
107 * -- return non-zero if the previous state was unmasked
108 */
109
110 #if (defined(__i386__) || defined(__x86_64__)) && defined(__GNUC__)
111
unmask_x87(void)112 static void unmask_x87(void)
113 {
114 unsigned short cw;
115
116 __asm__ __volatile__("fstcw %0" : "=m"(cw));
117 cw &= ~(0x01|0x04|0x08); /* unmask IM, ZM, OM */
118 __asm__ __volatile__("fldcw %0" : : "m"(cw));
119 }
120
mask_x87(void)121 static int mask_x87(void)
122 {
123 unsigned short cw;
124 int unmasked;
125
126 __asm__ __volatile__("fstcw %0" : "=m"(cw));
127 unmasked = (cw & (0x01|0x04|0x08)) == 0;
128 /* or just set cw = 0x37f */
129 cw |= (0x01|0x04|0x08); /* mask IM, ZM, OM */
130 __asm__ __volatile__("fldcw %0" : : "m"(cw));
131 return unmasked;
132 }
133
unmask_sse2(void)134 static void unmask_sse2(void)
135 {
136 unsigned int mxcsr;
137
138 __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
139 mxcsr &= ~(0x003F|0x0680); /* clear exn flags, unmask OM, ZM, IM (not PM, UM, DM) */
140 __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
141 }
142
mask_sse2(void)143 static int mask_sse2(void)
144 {
145 unsigned int mxcsr;
146 int unmasked;
147
148 __asm__ __volatile__("stmxcsr %0" : "=m"(mxcsr));
149 unmasked = (mxcsr & 0x0680) == 0;
150 /* or just set mxcsr = 0x1f80 */
151 mxcsr &= ~0x003F; /* clear exn flags */
152 mxcsr |= 0x0680; /* mask OM, ZM, IM (not PM, UM, DM) */
153 __asm__ __volatile__("ldmxcsr %0" : : "m"(mxcsr));
154 return unmasked;
155 }
156
157 #if defined(__x86_64__)
158
cpu_has_sse2(void)159 static ERTS_INLINE int cpu_has_sse2(void) { return 1; }
160
161 #else /* !__x86_64__ */
162
163 /*
164 * Check if an x86-32 processor has SSE2.
165 */
xor_eflags(unsigned int mask)166 static unsigned int xor_eflags(unsigned int mask)
167 {
168 unsigned int eax, edx;
169
170 eax = mask; /* eax = mask */
171 __asm__("pushfl\n\t"
172 "popl %0\n\t" /* edx = original EFLAGS */
173 "xorl %0, %1\n\t" /* eax = mask ^ EFLAGS */
174 "pushl %1\n\t"
175 "popfl\n\t" /* new EFLAGS = mask ^ original EFLAGS */
176 "pushfl\n\t"
177 "popl %1\n\t" /* eax = new EFLAGS */
178 "xorl %0, %1\n\t" /* eax = new EFLAGS ^ old EFLAGS */
179 "pushl %0\n\t"
180 "popfl" /* restore original EFLAGS */
181 : "=d"(edx), "=a"(eax)
182 : "1"(eax));
183 return eax;
184 }
185
cpuid_eax(unsigned int op)186 static ERTS_INLINE unsigned int cpuid_eax(unsigned int op)
187 {
188 unsigned int eax, save_ebx;
189
190 /* In PIC mode i386 reserves EBX. So we must save
191 and restore it ourselves to not upset gcc. */
192 __asm__(
193 "movl %%ebx, %1\n\t"
194 "cpuid\n\t"
195 "movl %1, %%ebx"
196 : "=a"(eax), "=m"(save_ebx)
197 : "0"(op)
198 : "cx", "dx");
199 return eax;
200 }
201
cpuid_edx(unsigned int op)202 static ERTS_INLINE unsigned int cpuid_edx(unsigned int op)
203 {
204 unsigned int eax, edx, save_ebx;
205
206 /* In PIC mode i386 reserves EBX. So we must save
207 and restore it ourselves to not upset gcc. */
208 __asm__(
209 "movl %%ebx, %2\n\t"
210 "cpuid\n\t"
211 "movl %2, %%ebx"
212 : "=a"(eax), "=d"(edx), "=m"(save_ebx)
213 : "0"(op)
214 : "cx");
215 return edx;
216 }
217
218 /* The AC bit, bit #18, is a new bit introduced in the EFLAGS
219 * register on the Intel486 processor to generate alignment
220 * faults. This bit cannot be set on the Intel386 processor.
221 */
is_386(void)222 static ERTS_INLINE int is_386(void)
223 {
224 return ((xor_eflags(1<<18) >> 18) & 1) == 0;
225 }
226
227 /* Newer x86 processors have a CPUID instruction, as indicated by
228 * the ID bit (#21) in EFLAGS being modifiable.
229 */
has_CPUID(void)230 static ERTS_INLINE int has_CPUID(void)
231 {
232 return (xor_eflags(1<<21) >> 21) & 1;
233 }
234
cpu_has_sse2(void)235 static int cpu_has_sse2(void)
236 {
237 unsigned int maxlev, features;
238 static int has_sse2 = -1;
239
240 if (has_sse2 >= 0)
241 return has_sse2;
242 has_sse2 = 0;
243
244 if (is_386())
245 return 0;
246 if (!has_CPUID())
247 return 0;
248 maxlev = cpuid_eax(0);
249 /* Intel A-step Pentium had a preliminary version of CPUID.
250 It also didn't have SSE2. */
251 if ((maxlev & 0xFFFFFF00) == 0x0500)
252 return 0;
253 /* If max level is zero then CPUID cannot report any features. */
254 if (maxlev == 0)
255 return 0;
256 features = cpuid_edx(1);
257 has_sse2 = (features & (1 << 26)) != 0;
258
259 return has_sse2;
260 }
261 #endif /* !__x86_64__ */
262
unmask_fpe_internal(void)263 static void unmask_fpe_internal(void)
264 {
265 unmask_x87();
266 if (cpu_has_sse2())
267 unmask_sse2();
268 }
269
unmask_fpe(void)270 static void unmask_fpe(void)
271 {
272 __asm__ __volatile__("fnclex");
273 unmask_fpe_internal();
274 }
275
mask_fpe(void)276 static int mask_fpe(void)
277 {
278 int unmasked;
279
280 unmasked = mask_x87();
281 if (cpu_has_sse2())
282 unmasked |= mask_sse2();
283 return unmasked;
284 }
285
erts_restore_fpu(void)286 void erts_restore_fpu(void)
287 {
288 __asm__ __volatile__("fninit");
289 unmask_fpe_internal();
290 }
291
292 #elif defined(__sparc__) && defined(__linux__)
293
294 #if defined(__arch64__)
295 #define LDX "ldx"
296 #define STX "stx"
297 #else
298 #define LDX "ld"
299 #define STX "st"
300 #endif
301
unmask_fpe(void)302 static void unmask_fpe(void)
303 {
304 unsigned long fsr;
305
306 __asm__(STX " %%fsr, %0" : "=m"(fsr));
307 fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */
308 fsr |= (0x1AUL << 23); /* enable NV, OF, DZ exceptions */
309 __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
310 }
311
mask_fpe(void)312 static int mask_fpe(void)
313 {
314 unsigned long fsr;
315 int unmasked;
316
317 __asm__(STX " %%fsr, %0" : "=m"(fsr));
318 unmasked = ((fsr >> 23) & 0x1A) == 0x1A;
319 fsr &= ~(0x1FUL << 23); /* clear FSR[TEM] field */
320 __asm__ __volatile__(LDX " %0, %%fsr" : : "m"(fsr));
321 return unmasked;
322 }
323
324 #elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__))
325
326 #if defined(__linux__)
327 #include <sys/prctl.h>
328
set_fpexc_precise(void)329 static void set_fpexc_precise(void)
330 {
331 if (prctl(PR_SET_FPEXC, PR_FP_EXC_PRECISE) < 0) {
332 perror("PR_SET_FPEXC");
333 exit(1);
334 }
335 }
336
337 #elif defined(__DARWIN__)
338
339 #include <mach/mach.h>
340 #include <pthread.h>
341
342 /*
343 * FE0 FE1 MSR bits
344 * 0 0 floating-point exceptions disabled
345 * 0 1 floating-point imprecise nonrecoverable
346 * 1 0 floating-point imprecise recoverable
347 * 1 1 floating-point precise mode
348 *
349 * Apparently:
350 * - Darwin 5.5 (MacOS X <= 10.1) starts with FE0 == FE1 == 0,
351 * and resets FE0 and FE1 to 0 after each SIGFPE.
352 * - Darwin 6.0 (MacOS X 10.2) starts with FE0 == FE1 == 1,
353 * and does not reset FE0 or FE1 after a SIGFPE.
354 */
355 #define FE0_MASK (1<<11)
356 #define FE1_MASK (1<<8)
357
358 /* a thread cannot get or set its own MSR bits */
fpu_fpe_enable(void * arg)359 static void *fpu_fpe_enable(void *arg)
360 {
361 thread_t t = *(thread_t*)arg;
362 struct ppc_thread_state state;
363 unsigned int state_size = PPC_THREAD_STATE_COUNT;
364
365 if (thread_get_state(t, PPC_THREAD_STATE, (natural_t*)&state, &state_size) != KERN_SUCCESS) {
366 perror("thread_get_state");
367 exit(1);
368 }
369 if ((state.srr1 & (FE1_MASK|FE0_MASK)) != (FE1_MASK|FE0_MASK)) {
370 #if 1
371 /* This would also have to be performed in the SIGFPE handler
372 to work around the MSR reset older Darwin releases do. */
373 state.srr1 |= (FE1_MASK|FE0_MASK);
374 thread_set_state(t, PPC_THREAD_STATE, (natural_t*)&state, state_size);
375 #else
376 fprintf(stderr, "srr1 == 0x%08x, your Darwin is too old\n", state.srr1);
377 exit(1);
378 #endif
379 }
380 return NULL; /* Ok, we appear to be on Darwin 6.0 or later */
381 }
382
set_fpexc_precise(void)383 static void set_fpexc_precise(void)
384 {
385 thread_t self = mach_thread_self();
386 pthread_t enabler;
387
388 if (pthread_create(&enabler, NULL, fpu_fpe_enable, &self)) {
389 perror("pthread_create");
390 } else if (pthread_join(enabler, NULL)) {
391 perror("pthread_join");
392 }
393 }
394
395 #endif
396
set_fpscr(unsigned int fpscr)397 static void set_fpscr(unsigned int fpscr)
398 {
399 union {
400 double d;
401 unsigned int fpscr[2];
402 } u;
403
404 u.fpscr[0] = 0xFFF80000;
405 u.fpscr[1] = fpscr;
406 __asm__ __volatile__("mtfsf 255,%0" : : "f"(u.d));
407 }
408
get_fpscr(void)409 static unsigned int get_fpscr(void)
410 {
411 union {
412 double d;
413 unsigned int fpscr[2];
414 } u;
415
416 __asm__("mffs %0" : "=f"(u.d));
417 return u.fpscr[1];
418 }
419
unmask_fpe(void)420 static void unmask_fpe(void)
421 {
422 set_fpexc_precise();
423 set_fpscr(0x80|0x40|0x10); /* VE, OE, ZE; not UE or XE */
424 }
425
mask_fpe(void)426 static int mask_fpe(void)
427 {
428 int unmasked;
429
430 unmasked = (get_fpscr() & (0x80|0x40|0x10)) == (0x80|0x40|0x10);
431 set_fpscr(0x00);
432 return unmasked;
433 }
434
435 #else /* !(x86 || (sparc && linux) || (powerpc && (linux || darwin))) */
436
unmask_fpe(void)437 static void unmask_fpe(void)
438 {
439 fpsetmask(FP_X_INV | FP_X_OFL | FP_X_DZ);
440 }
441
mask_fpe(void)442 static int mask_fpe(void)
443 {
444 const fp_except unmasked_mask = FP_X_INV | FP_X_OFL | FP_X_DZ;
445 fp_except old_mask;
446
447 old_mask = fpsetmask(0);
448 return (old_mask & unmasked_mask) == unmasked_mask;
449 }
450
451 #endif
452
453 /*
454 * Define a processor and OS-specific SIGFPE handler.
455 *
456 * The effect of receiving a SIGFPE should be:
457 * 1. Update the processor context:
458 * a) on x86: mask FP exceptions, do not skip faulting instruction
459 * b) on SPARC and PowerPC: unmask FP exceptions, skip faulting instruction
460 * 2. call set_current_fp_exception with the PC of the faulting instruction
461 */
462
463 #if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
464
465 #if defined(__linux__) && defined(__i386__)
466 #if !defined(X86_FXSR_MAGIC)
467 #define X86_FXSR_MAGIC 0x0000
468 #endif
469 #elif defined(__FreeBSD__) && defined(__x86_64__)
470 #include <sys/types.h>
471 #include <machine/fpu.h>
472 #elif defined(__FreeBSD__) && defined(__i386__)
473 #include <sys/types.h>
474 #include <machine/npx.h>
475 #elif defined(__DARWIN__)
476 #include <machine/signal.h>
477 #elif defined(__OpenBSD__) && defined(__x86_64__)
478 #include <sys/types.h>
479 #include <machine/fpu.h>
480 #endif
481 #if !(defined(__OpenBSD__) && defined(__x86_64__))
482 #include <ucontext.h>
483 #endif
484 #include <string.h>
485
486 #if defined(__linux__) && defined(__x86_64__)
487 #define mc_pc(mc) ((mc)->gregs[REG_RIP])
488 #elif defined(__linux__) && defined(__i386__)
489 #define mc_pc(mc) ((mc)->gregs[REG_EIP])
490 #elif defined(__DARWIN__)
491 # error "Floating-point exceptions not supported on MacOS X"
492 #elif defined(__FreeBSD__) && defined(__x86_64__)
493 #define mc_pc(mc) ((mc)->mc_rip)
494 #elif defined(__FreeBSD__) && defined(__i386__)
495 #define mc_pc(mc) ((mc)->mc_eip)
496 #elif defined(__NetBSD__) && defined(__x86_64__)
497 #define mc_pc(mc) ((mc)->__gregs[_REG_RIP])
498 #elif defined(__NetBSD__) && defined(__i386__)
499 #define mc_pc(mc) ((mc)->__gregs[_REG_EIP])
500 #elif defined(__OpenBSD__) && defined(__x86_64__)
501 #define mc_pc(mc) ((mc)->sc_rip)
502 #elif defined(__sun__) && defined(__x86_64__)
503 #define mc_pc(mc) ((mc)->gregs[REG_RIP])
504 #endif
505
fpe_sig_action(int sig,siginfo_t * si,void * puc)506 static void fpe_sig_action(int sig, siginfo_t *si, void *puc)
507 {
508 ucontext_t *uc = puc;
509 unsigned long pc;
510
511 #if defined(__linux__) && defined(__x86_64__)
512 mcontext_t *mc = &uc->uc_mcontext;
513 fpregset_t fpstate = mc->fpregs;
514 pc = mc_pc(mc);
515 /* A failed SSE2 instruction will restart. To avoid
516 looping we mask SSE2 exceptions now and unmask them
517 again later in erts_check_fpe()/erts_restore_fpu().
518 On RISCs we update PC to skip the failed instruction,
519 but the ever increasing complexity of the x86 instruction
520 set encoding makes that a poor solution here. */
521 fpstate->mxcsr = 0x1F80;
522 fpstate->swd &= ~0xFF;
523 #elif defined(__linux__) && defined(__i386__)
524 mcontext_t *mc = &uc->uc_mcontext;
525 fpregset_t fpstate = mc->fpregs;
526 pc = mc_pc(mc);
527 if ((fpstate->status >> 16) == X86_FXSR_MAGIC)
528 ((struct _fpstate*)fpstate)->mxcsr = 0x1F80;
529 fpstate->sw &= ~0xFF;
530 #elif defined(__linux__) && defined(__sparc__) && defined(__arch64__)
531 /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
532 struct sigcontext *sc = (struct sigcontext*)puc;
533 pc = sc->sigc_regs.tpc;
534 sc->sigc_regs.tpc = sc->sigc_regs.tnpc;
535 sc->sigc_regs.tnpc += 4;
536 #elif defined(__linux__) && defined(__sparc__)
537 /* on SPARC the 3rd parameter points to a sigcontext not a ucontext */
538 struct sigcontext *sc = (struct sigcontext*)puc;
539 pc = sc->si_regs.pc;
540 sc->si_regs.pc = sc->si_regs.npc;
541 sc->si_regs.npc = (unsigned long)sc->si_regs.npc + 4;
542 #elif defined(__linux__) && defined(__powerpc__)
543 #if defined(__powerpc64__)
544 mcontext_t *mc = &uc->uc_mcontext;
545 unsigned long *regs = &mc->gp_regs[0];
546 #else
547 mcontext_t *mc = uc->uc_mcontext.uc_regs;
548 unsigned long *regs = &mc->gregs[0];
549 #endif
550 pc = regs[PT_NIP];
551 regs[PT_NIP] += 4;
552 regs[PT_FPSCR] = 0x80|0x40|0x10; /* VE, OE, ZE; not UE or XE */
553 #elif defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__))
554 # error "Floating-point exceptions not supported on MacOS X"
555 #elif defined(__DARWIN__) && defined(__ppc__)
556 mcontext_t mc = uc->uc_mcontext;
557 pc = mc->ss.srr0;
558 mc->ss.srr0 += 4;
559 mc->fs.fpscr = 0x80|0x40|0x10;
560 #elif defined(__FreeBSD__) && defined(__x86_64__)
561 mcontext_t *mc = &uc->uc_mcontext;
562 struct savefpu *savefpu = (struct savefpu*)&mc->mc_fpstate;
563 struct envxmm *envxmm = &savefpu->sv_env;
564 pc = mc_pc(mc);
565 envxmm->en_mxcsr = 0x1F80;
566 envxmm->en_sw &= ~0xFF;
567 #elif defined(__FreeBSD__) && defined(__i386__)
568 mcontext_t *mc = &uc->uc_mcontext;
569 union savefpu *savefpu = (union savefpu*)&mc->mc_fpstate;
570 pc = mc_pc(mc);
571 if (mc->mc_fpformat == _MC_FPFMT_XMM) {
572 struct envxmm *envxmm = &savefpu->sv_xmm.sv_env;
573 envxmm->en_mxcsr = 0x1F80;
574 envxmm->en_sw &= ~0xFF;
575 } else {
576 struct env87 *env87 = &savefpu->sv_87.sv_env;
577 env87->en_sw &= ~0xFF;
578 }
579 #elif defined(__NetBSD__) && defined(__x86_64__)
580 mcontext_t *mc = &uc->uc_mcontext;
581 struct fxsave64 *fxsave = (struct fxsave64 *)&mc->__fpregs;
582 pc = mc_pc(mc);
583 fxsave->fx_mxcsr = 0x1F80;
584 fxsave->fx_fsw &= ~0xFF;
585 #elif defined(__NetBSD__) && defined(__i386__)
586 mcontext_t *mc = &uc->uc_mcontext;
587 pc = mc_pc(mc);
588 if (uc->uc_flags & _UC_FXSAVE) {
589 struct envxmm *envxmm = (struct envxmm *)&mc->__fpregs;
590 envxmm->en_mxcsr = 0x1F80;
591 envxmm->en_sw &= ~0xFF;
592 } else {
593 struct env87 *env87 = (struct env87 *)&mc->__fpregs;
594 env87->en_sw &= ~0xFF;
595 }
596 #elif defined(__OpenBSD__) && defined(__x86_64__)
597 struct fxsave64 *fxsave = uc->sc_fpstate;
598 pc = mc_pc(uc);
599 fxsave->fx_mxcsr = 0x1F80;
600 fxsave->fx_fsw &= ~0xFF;
601 #elif defined(__sun__) && defined(__x86_64__)
602 mcontext_t *mc = &uc->uc_mcontext;
603 struct fpchip_state *fpstate = &mc->fpregs.fp_reg_set.fpchip_state;
604 pc = mc_pc(mc);
605 fpstate->mxcsr = 0x1F80;
606 fpstate->sw &= ~0xFF;
607 #endif
608 #if 0
609 {
610 char buf[128];
611 snprintf(buf, sizeof buf, "%s: FPE at %p\r\n", __FUNCTION__, (void*)pc);
612 write(2, buf, strlen(buf));
613 }
614 #endif
615 set_current_fp_exception(pc);
616 }
617
erts_thread_catch_fp_exceptions(void)618 static void erts_thread_catch_fp_exceptions(void)
619 {
620 struct sigaction act;
621 memset(&act, 0, sizeof act);
622 act.sa_sigaction = fpe_sig_action;
623 act.sa_flags = SA_SIGINFO;
624 sigaction(SIGFPE, &act, NULL);
625 unmask_fpe();
626 }
627
628 #else /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
629
fpe_sig_handler(int sig)630 static void fpe_sig_handler(int sig)
631 {
632 set_current_fp_exception(1); /* XXX: convert to sigaction so we can get the trap PC */
633 }
634
erts_thread_catch_fp_exceptions(void)635 static void erts_thread_catch_fp_exceptions(void)
636 {
637 sys_signal(SIGFPE, fpe_sig_handler);
638 unmask_fpe();
639 }
640
641 #endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
642
643 /* once-only initialisation early in the main thread */
erts_sys_init_float(void)644 void erts_sys_init_float(void)
645 {
646 erts_init_fp_exception();
647 erts_thread_catch_fp_exceptions();
648 erts_printf_block_fpe = erts_sys_block_fpe;
649 erts_printf_unblock_fpe = erts_sys_unblock_fpe;
650 }
651
652 #endif /* NO_FPE_SIGNALS */
653
erts_thread_init_float(void)654 void erts_thread_init_float(void)
655 {
656 /* This allows Erlang schedulers to leave Erlang-process context
657 and still have working FP exceptions. XXX: is this needed? */
658 erts_thread_init_fp_exception();
659
660 #ifndef NO_FPE_SIGNALS
661 /* NOTE:
662 * erts_thread_disable_fpe() is called in all threads at
663 * creation. We at least need to call unmask_fpe()
664 */
665 #if defined(__DARWIN__) || defined(__FreeBSD__)
666 /* Darwin (7.9.0) does not appear to propagate FP exception settings
667 to a new thread from its parent. So if we want FP exceptions, we
668 must manually re-enable them in each new thread.
669 FreeBSD 6.1 appears to suffer from a similar issue. */
670 erts_thread_catch_fp_exceptions();
671 #else
672 unmask_fpe();
673 #endif
674
675 #endif
676 }
677
erts_thread_disable_fpe(void)678 void erts_thread_disable_fpe(void)
679 {
680 #if !defined(NO_FPE_SIGNALS)
681 (void)mask_fpe();
682 #endif
683 }
684
685 #if !defined(NO_FPE_SIGNALS)
erts_sys_block_fpe(void)686 int erts_sys_block_fpe(void)
687 {
688 return mask_fpe();
689 }
690
erts_sys_unblock_fpe(int unmasked)691 void erts_sys_unblock_fpe(int unmasked)
692 {
693 if (unmasked)
694 unmask_fpe();
695 }
696 #endif
697
698 /* The following check is incorporated from the Vee machine */
699
700 #define ISDIGIT(d) ((d) >= '0' && (d) <= '9')
701
702 /*
703 ** Convert a double to ascii format 0.dddde[+|-]ddd
704 ** return number of characters converted or -1 if error.
705 **
706 ** These two functions should maybe use localeconv() to pick up
707 ** the current radix character, but since it is uncertain how
708 ** expensive such a system call is, and since no-one has heard
709 ** of other radix characters than '.' and ',' an ad-hoc
710 ** low execution time solution is used instead.
711 */
712
713 int
sys_double_to_chars_ext(double fp,char * buffer,size_t buffer_size,size_t decimals)714 sys_double_to_chars_ext(double fp, char *buffer, size_t buffer_size, size_t decimals)
715 {
716 char *s = buffer;
717
718 if (erts_snprintf(buffer, buffer_size, "%.*e", decimals, fp) >= buffer_size)
719 return -1;
720 /* Search upto decimal point */
721 if (*s == '+' || *s == '-') s++;
722 while (ISDIGIT(*s)) s++;
723 if (*s == ',') *s++ = '.'; /* Replace ',' with '.' */
724 /* Scan to end of string */
725 while (*s) s++;
726 return s-buffer; /* i.e strlen(buffer) */
727 }
728
729 /* Float conversion */
730
731 int
sys_chars_to_double(char * buf,double * fp)732 sys_chars_to_double(char* buf, double* fp)
733 {
734 #ifndef NO_FPE_SIGNALS
735 volatile unsigned long *fpexnp = erts_get_current_fp_exception();
736 #endif
737 char *s = buf, *t, *dp;
738
739 /* Robert says that something like this is what he really wanted:
740 * (The [.,] radix test is NOT what Robert wanted - it was added later)
741 *
742 * 7 == sscanf(Tbuf, "%[+-]%[0-9][.,]%[0-9]%[eE]%[+-]%[0-9]%s", ....);
743 * if (*s2 == 0 || *s3 == 0 || *s4 == 0 || *s6 == 0 || *s7)
744 * break;
745 */
746
747 /* Scan string to check syntax. */
748 if (*s == '+' || *s == '-') s++;
749 if (!ISDIGIT(*s)) /* Leading digits. */
750 return -1;
751 while (ISDIGIT(*s)) s++;
752 if (*s != '.' && *s != ',') /* Decimal part. */
753 return -1;
754 dp = s++; /* Remember decimal point pos just in case */
755 if (!ISDIGIT(*s))
756 return -1;
757 while (ISDIGIT(*s)) s++;
758 if (*s == 'e' || *s == 'E') {
759 /* There is an exponent. */
760 s++;
761 if (*s == '+' || *s == '-') s++;
762 if (!ISDIGIT(*s))
763 return -1;
764 while (ISDIGIT(*s)) s++;
765 }
766 if (*s) /* That should be it */
767 return -1;
768
769 #ifdef NO_FPE_SIGNALS
770 errno = 0;
771 #endif
772 __ERTS_FP_CHECK_INIT(fpexnp);
773 *fp = strtod(buf, &t);
774 __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
775 if (t != s) { /* Whole string not scanned */
776 /* Try again with other radix char */
777 *dp = (*dp == '.') ? ',' : '.';
778 errno = 0;
779 __ERTS_FP_CHECK_INIT(fpexnp);
780 *fp = strtod(buf, &t);
781 __ERTS_FP_ERROR_THOROUGH(fpexnp, *fp, return -1);
782 }
783
784 #ifdef NO_FPE_SIGNALS
785 if (errno == ERANGE) {
786 if (*fp == HUGE_VAL || *fp == -HUGE_VAL) {
787 /* overflow, should give error */
788 return -1;
789 } else if (t == s && *fp == 0.0) {
790 /* This should give 0.0 - OTP-7178 */
791 errno = 0;
792
793 } else if (*fp == 0.0) {
794 return -1;
795 }
796 }
797 #endif
798 return 0;
799 }
800
801 #ifdef USE_MATHERR
802
803 int
matherr(struct exception * exc)804 matherr(struct exception *exc)
805 {
806 return 1;
807 }
808
809 #endif
810