xref: /illumos-gate/usr/src/lib/libm/common/m9x/__fex_hdlr.c (revision b515258426fed6c7311fd3f1dea697cfbd4085c6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
24  */
25 /*
26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #include "fenv_synonyms.h"
31 #undef lint
32 #include <signal.h>
33 #include <siginfo.h>
34 #include <ucontext.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <thread.h>
39 #include <math.h>
40 #if defined(__SUNPRO_C)
41 #include <sunmath.h>
42 #endif
43 #include <fenv.h>
44 #include "fex_handler.h"
45 #include "fenv_inlines.h"
46 
47 #if defined(__sparc) && !defined(__sparcv9)
48 #include <sys/procfs.h>
49 #endif
50 
51 /* 2.x signal.h doesn't declare sigemptyset or sigismember
52    if they're #defined (see sys/signal.h) */
53 extern int sigemptyset(sigset_t *);
54 extern int sigismember(const sigset_t *, int);
55 
56 /* external globals */
57 void (*__mt_fex_sync)() = NULL; /* for synchronization with libmtsk */
58 #pragma weak __mt_fex_sync
59 
60 #ifdef LIBM_MT_FEX_SYNC
61 void (*__libm_mt_fex_sync)() = NULL; /* new, improved version of above */
62 #pragma weak __libm_mt_fex_sync
63 #endif
64 
65 /* private variables */
66 static fex_handler_t main_handlers;
67 static int handlers_initialized = 0;
68 static thread_key_t handlers_key;
69 static mutex_t handlers_key_lock = DEFAULTMUTEX;
70 
71 static struct sigaction oact = { 0, SIG_DFL };
72 static mutex_t hdlr_lock = DEFAULTMUTEX;
73 static int hdlr_installed = 0;
74 
75 /* private const data */
76 static const int te_bit[FEX_NUM_EXC] = {
77 	1 << fp_trap_inexact,
78 	1 << fp_trap_division,
79 	1 << fp_trap_underflow,
80 	1 << fp_trap_overflow,
81 	1 << fp_trap_invalid,
82 	1 << fp_trap_invalid,
83 	1 << fp_trap_invalid,
84 	1 << fp_trap_invalid,
85 	1 << fp_trap_invalid,
86 	1 << fp_trap_invalid,
87 	1 << fp_trap_invalid,
88 	1 << fp_trap_invalid
89 };
90 
91 /*
92 *  Return the traps to be enabled given the current handling modes
93 *  and flags
94 */
95 static int
96 __fex_te_needed(struct fex_handler_data *thr_handlers, unsigned long fsr)
97 {
98 	int		i, ex, te;
99 
100 	/* set traps for handling modes */
101 	te = 0;
102 	for (i = 0; i < FEX_NUM_EXC; i++)
103 		if (thr_handlers[i].__mode != FEX_NONSTOP)
104 			te |= te_bit[i];
105 
106 	/* add traps for retrospective diagnostics */
107 	if (fex_get_log()) {
108 		ex = (int)__fenv_get_ex(fsr);
109 		if (!(ex & FE_INEXACT))
110 			te |= (1 << fp_trap_inexact);
111 		if (!(ex & FE_UNDERFLOW))
112 			te |= (1 << fp_trap_underflow);
113 		if (!(ex & FE_OVERFLOW))
114 			te |= (1 << fp_trap_overflow);
115 		if (!(ex & FE_DIVBYZERO))
116 			te |= (1 << fp_trap_division);
117 		if (!(ex & FE_INVALID))
118 			te |= (1 << fp_trap_invalid);
119 	}
120 
121 	return te;
122 }
123 
124 /*
125 *  The following function synchronizes with libmtsk (SPARC only, for now)
126 */
127 static void
128 __fex_sync_with_libmtsk(int begin, int master)
129 {
130 	static fenv_t master_env;
131 	static int env_initialized = 0;
132 	static mutex_t env_lock = DEFAULTMUTEX;
133 
134 	if (begin) {
135 		mutex_lock(&env_lock);
136 		if (master) {
137 			(void) fegetenv(&master_env);
138 			env_initialized = 1;
139 		}
140 		else if (env_initialized)
141 			(void) fesetenv(&master_env);
142 		mutex_unlock(&env_lock);
143 	}
144 	else if (master && fex_get_log())
145 		__fex_update_te();
146 }
147 
148 #ifdef LIBM_MT_FEX_SYNC
149 /*
150 *  The following function may be used for synchronization with any
151 *  internal project that manages multiple threads
152 */
153 enum __libm_mt_fex_sync_actions {
154 	__libm_mt_fex_start_master = 0,
155 	__libm_mt_fex_start_slave,
156 	__libm_mt_fex_finish_master,
157 	__libm_mt_fex_finish_slave
158 };
159 
160 struct __libm_mt_fex_sync_data {
161 	fenv_t	master_env;
162 	int		initialized;
163 	mutex_t	lock;
164 };
165 
166 static void
167 __fex_sync_with_threads(enum __libm_mt_fex_sync_actions action,
168 	struct __libm_mt_fex_sync_data *thr_env)
169 {
170 	switch (action) {
171 	case __libm_mt_fex_start_master:
172 		mutex_lock(&thr_env->lock);
173 		(void) fegetenv(&thr_env->master_env);
174 		thr_env->initialized = 1;
175 		mutex_unlock(&thr_env->lock);
176 		break;
177 
178 	case __libm_mt_fex_start_slave:
179 		mutex_lock(&thr_env->lock);
180 		if (thr_env->initialized)
181 			(void) fesetenv(&thr_env->master_env);
182 		mutex_unlock(&thr_env->lock);
183 		break;
184 
185 	case __libm_mt_fex_finish_master:
186 #if defined(__x86)
187 		__fex_update_te();
188 #else
189 		if (fex_get_log())
190 			__fex_update_te();
191 #endif
192 		break;
193 
194 	case __libm_mt_fex_finish_slave:
195 #if defined(__x86)
196 		/* clear traps, making all accrued flags visible in status word */
197 		{
198 			unsigned long   fsr;
199 			__fenv_getfsr(&fsr);
200 			__fenv_set_te(fsr, 0);
201 			__fenv_setfsr(&fsr);
202 		}
203 #endif
204 		break;
205 	}
206 }
207 #endif
208 
209 #if defined(__sparc)
210 
211 /*
212 *  Code for setting or clearing interval mode on US-III and above.
213 *  This is embedded as data so we don't have to mark the library
214 *  as a v8plusb/v9b object.  (I could have just used one entry and
215 *  modified the second word to set the bits I want, but that would
216 *  have required another mutex.)
217 */
218 static const unsigned int siam[][2] = {
219 	{ 0x81c3e008, 0x81b01020 }, /* retl, siam 0 */
220 	{ 0x81c3e008, 0x81b01024 }, /* retl, siam 4 */
221 	{ 0x81c3e008, 0x81b01025 }, /* retl, siam 5 */
222 	{ 0x81c3e008, 0x81b01026 }, /* retl, siam 6 */
223 	{ 0x81c3e008, 0x81b01027 }  /* retl, siam 7 */
224 };
225 
226 /*
227 *  If a handling mode is in effect, apply it; otherwise invoke the
228 *  saved handler
229 */
230 static void
231 __fex_hdlr(int sig, siginfo_t *sip, ucontext_t *uap)
232 {
233 	struct fex_handler_data	*thr_handlers;
234 	struct sigaction	act;
235 	void			(*handler)(), (*siamp)();
236 	int			mode, i;
237 	enum fex_exception	e;
238 	fex_info_t		info;
239 	unsigned long		fsr, tmpfsr, addr;
240 	unsigned int		gsr;
241 
242 	/* determine which exception occurred */
243 	switch (sip->si_code) {
244 	case FPE_FLTDIV:
245 		e = fex_division;
246 		break;
247 	case FPE_FLTOVF:
248 		e = fex_overflow;
249 		break;
250 	case FPE_FLTUND:
251 		e = fex_underflow;
252 		break;
253 	case FPE_FLTRES:
254 		e = fex_inexact;
255 		break;
256 	case FPE_FLTINV:
257 		if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
258 			goto not_ieee;
259 		break;
260 	default:
261 		/* not an IEEE exception */
262 		goto not_ieee;
263 	}
264 
265 	/* get the handling mode */
266 	mode = FEX_NOHANDLER;
267 	handler = oact.sa_handler; /* for log; just looking, no need to lock */
268 	thr_handlers = __fex_get_thr_handlers();
269 	if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
270 		mode = thr_handlers[(int)e].__mode;
271 		handler = thr_handlers[(int)e].__handler;
272 	}
273 
274 	/* make an entry in the log of retro. diag. if need be */
275 	i = ((int)uap->uc_mcontext.fpregs.fpu_fsr >> 5) & 0x1f;
276 	__fex_mklog(uap, (char *)sip->si_addr, i, e, mode, (void *)handler);
277 
278 	/* handle the exception based on the mode */
279 	if (mode == FEX_NOHANDLER)
280 		goto not_ieee;
281 	else if (mode == FEX_ABORT)
282 		abort();
283 	else if (mode == FEX_SIGNAL) {
284 		handler(sig, sip, uap);
285 		return;
286 	}
287 
288 	/* custom or nonstop mode; disable traps and clear flags */
289 	__fenv_getfsr(&fsr);
290 	__fenv_set_te(fsr, 0);
291 	__fenv_set_ex(fsr, 0);
292 
293 	/* if interval mode was set, clear it, then substitute the
294 	   interval rounding direction and clear ns mode in the fsr */
295 #ifdef __sparcv9
296 	gsr = uap->uc_mcontext.asrs[3];
297 #else
298 	gsr = 0;
299 	if (uap->uc_mcontext.xrs.xrs_id == XRS_ID)
300 		gsr = (*(unsigned long long*)((prxregset_t*)uap->uc_mcontext.
301 		    xrs.xrs_ptr)->pr_un.pr_v8p.pr_filler);
302 #endif
303 	gsr = (gsr >> 25) & 7;
304 	if (gsr & 4) {
305 		siamp = (void (*)()) siam[0];
306 		siamp();
307 		tmpfsr = fsr;
308 		fsr = (fsr & ~0xc0400000ul) | ((gsr & 3) << 30);
309 	}
310 	__fenv_setfsr(&fsr);
311 
312 	/* decode the operation */
313 	__fex_get_op(sip, uap, &info);
314 
315 	/* if a custom mode handler is installed, invoke it */
316 	if (mode == FEX_CUSTOM) {
317 		/* if we got here from feraiseexcept, pass dummy info */
318 		addr = (unsigned long)sip->si_addr;
319 		if (addr >= (unsigned long)feraiseexcept &&
320 		    addr < (unsigned long)fetestexcept) {
321 			info.op = fex_other;
322 			info.op1.type = info.op2.type = info.res.type =
323 			    fex_nodata;
324 		}
325 
326 		/* restore interval mode if it was set, and put the original
327 		   rounding direction and ns mode back in the fsr */
328 		if (gsr & 4) {
329 			__fenv_setfsr(&tmpfsr);
330 			siamp = (void (*)()) siam[1 + (gsr & 3)];
331 			siamp();
332 		}
333 
334 		handler(1 << (int)e, &info);
335 
336 		/* restore modes in case the user's handler changed them */
337 		if (gsr & 4) {
338 			siamp = (void (*)()) siam[0];
339 			siamp();
340 		}
341 		__fenv_setfsr(&fsr);
342 	}
343 
344 	/* stuff the result */
345 	__fex_st_result(sip, uap, &info);
346 
347 	/* "or" in any exception flags and update traps */
348 	fsr = uap->uc_mcontext.fpregs.fpu_fsr;
349 	fsr |= ((info.flags & 0x1f) << 5);
350 	i = __fex_te_needed(thr_handlers, fsr);
351 	__fenv_set_te(fsr, i);
352 	uap->uc_mcontext.fpregs.fpu_fsr = fsr;
353 	return;
354 
355 not_ieee:
356 	/* revert to the saved handler (if any) */
357 	mutex_lock(&hdlr_lock);
358 	act = oact;
359 	mutex_unlock(&hdlr_lock);
360 	switch ((unsigned long)act.sa_handler) {
361 	case (unsigned long)SIG_DFL:
362 		/* simulate trap with no handler installed */
363 		sigaction(SIGFPE, &act, NULL);
364 		kill(getpid(), SIGFPE);
365 		break;
366 #if !defined(__lint)
367 	case (unsigned long)SIG_IGN:
368 		break;
369 #endif
370 	default:
371 		act.sa_handler(sig, sip, uap);
372 	}
373 }
374 
375 #elif defined(__x86)
376 
377 #if defined(__amd64)
378 #define test_sse_hw	1
379 #else
380 extern int _sse_hw;
381 #define test_sse_hw	_sse_hw
382 #endif
383 
384 #if !defined(REG_PC)
385 #define REG_PC	EIP
386 #endif
387 
388 /*
389 *  If a handling mode is in effect, apply it; otherwise invoke the
390 *  saved handler
391 */
392 static void
393 __fex_hdlr(int sig, siginfo_t *sip, ucontext_t *uap)
394 {
395 	struct fex_handler_data	*thr_handlers;
396 	struct sigaction	act;
397 	void			(*handler)() = NULL, (*simd_handler[4])();
398 	int			mode, simd_mode[4], i, len, accrued, *ap;
399 	unsigned int		cwsw, oldcwsw, mxcsr, oldmxcsr;
400 	enum fex_exception	e, simd_e[4];
401 	fex_info_t		info, simd_info[4];
402 	unsigned long		addr;
403 	siginfo_t		osip = *sip;
404 	sseinst_t		inst;
405 
406 	/* check for an exception caused by an SSE instruction */
407 	if (!(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 0x80)) {
408 		len = __fex_parse_sse(uap, &inst);
409 		if (len == 0)
410 			goto not_ieee;
411 
412 		/* disable all traps and clear flags */
413 		__fenv_getcwsw(&oldcwsw);
414 		cwsw = (oldcwsw & ~0x3f) | 0x003f0000;
415 		__fenv_setcwsw(&cwsw);
416 		__fenv_getmxcsr(&oldmxcsr);
417 		mxcsr = (oldmxcsr & ~0x3f) | 0x1f80;
418 		__fenv_setmxcsr(&mxcsr);
419 
420 		if ((int)inst.op & SIMD) {
421 			__fex_get_simd_op(uap, &inst, simd_e, simd_info);
422 
423 			thr_handlers = __fex_get_thr_handlers();
424 			addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
425 			accrued = uap->uc_mcontext.fpregs.fp_reg_set.
426 			    fpchip_state.mxcsr;
427 
428 			e = (enum fex_exception)-1;
429 			mode = FEX_NONSTOP;
430 			for (i = 0; i < 4; i++) {
431 				if ((int)simd_e[i] < 0)
432 					continue;
433 
434 				e = simd_e[i];
435 				simd_mode[i] = FEX_NOHANDLER;
436 				simd_handler[i] = oact.sa_handler;
437 				if (thr_handlers &&
438 				    thr_handlers[(int)e].__mode !=
439 				    FEX_NOHANDLER) {
440 					simd_mode[i] =
441 					    thr_handlers[(int)e].__mode;
442 					simd_handler[i] =
443 					    thr_handlers[(int)e].__handler;
444 				}
445 				accrued &= ~te_bit[(int)e];
446 				switch (simd_mode[i]) {
447 				case FEX_ABORT:
448 					mode = FEX_ABORT;
449 					break;
450 				case FEX_SIGNAL:
451 					if (mode != FEX_ABORT)
452 						mode = FEX_SIGNAL;
453 					handler = simd_handler[i];
454 					break;
455 				case FEX_NOHANDLER:
456 					if (mode != FEX_ABORT && mode !=
457 					    FEX_SIGNAL)
458 						mode = FEX_NOHANDLER;
459 					break;
460 				}
461 			}
462 			if (e == (enum fex_exception)-1) {
463 				__fenv_setcwsw(&oldcwsw);
464 				__fenv_setmxcsr(&oldmxcsr);
465 				goto not_ieee;
466 			}
467 			accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
468 			    fpchip_state.status;
469 			ap = __fex_accrued();
470 			accrued |= *ap;
471 			accrued &= 0x3d;
472 
473 			for (i = 0; i < 4; i++) {
474 				if ((int)simd_e[i] < 0)
475 					continue;
476 
477 				__fex_mklog(uap, (char *)addr, accrued,
478 				    simd_e[i], simd_mode[i],
479 				    (void *)simd_handler[i]);
480 			}
481 
482 			if (mode == FEX_NOHANDLER) {
483 				__fenv_setcwsw(&oldcwsw);
484 				__fenv_setmxcsr(&oldmxcsr);
485 				goto not_ieee;
486 			} else if (mode == FEX_ABORT) {
487 				abort();
488 			} else if (mode == FEX_SIGNAL) {
489 				__fenv_setcwsw(&oldcwsw);
490 				__fenv_setmxcsr(&oldmxcsr);
491 				handler(sig, &osip, uap);
492 				return;
493 			}
494 
495 			*ap = 0;
496 			for (i = 0; i < 4; i++) {
497 				if ((int)simd_e[i] < 0)
498 					continue;
499 
500 				if (simd_mode[i] == FEX_CUSTOM) {
501 					handler(1 << (int)simd_e[i],
502 					    &simd_info[i]);
503 					__fenv_setcwsw(&cwsw);
504 					__fenv_setmxcsr(&mxcsr);
505 				}
506 			}
507 
508 			__fex_st_simd_result(uap, &inst, simd_e, simd_info);
509 			for (i = 0; i < 4; i++) {
510 				if ((int)simd_e[i] < 0)
511 					continue;
512 
513 				accrued |= simd_info[i].flags;
514 			}
515 
516 			if ((int)inst.op & INTREG) {
517 				/* set MMX mode */
518 #if defined(__amd64)
519 				uap->uc_mcontext.fpregs.fp_reg_set.
520 				    fpchip_state.sw &= ~0x3800;
521 				uap->uc_mcontext.fpregs.fp_reg_set.
522 				    fpchip_state.fctw = 0;
523 #else
524 				uap->uc_mcontext.fpregs.fp_reg_set.
525 				    fpchip_state.state[1] &= ~0x3800;
526 				uap->uc_mcontext.fpregs.fp_reg_set.
527 				    fpchip_state.state[2] = 0;
528 #endif
529 			}
530 		} else {
531 			e = __fex_get_sse_op(uap, &inst, &info);
532 			if ((int)e < 0) {
533 				__fenv_setcwsw(&oldcwsw);
534 				__fenv_setmxcsr(&oldmxcsr);
535 				goto not_ieee;
536 			}
537 
538 			mode = FEX_NOHANDLER;
539 			handler = oact.sa_handler;
540 			thr_handlers = __fex_get_thr_handlers();
541 			if (thr_handlers && thr_handlers[(int)e].__mode !=
542 			    FEX_NOHANDLER) {
543 				mode = thr_handlers[(int)e].__mode;
544 				handler = thr_handlers[(int)e].__handler;
545 			}
546 
547 			addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
548 			accrued = uap->uc_mcontext.fpregs.fp_reg_set.
549 			    fpchip_state.mxcsr & ~te_bit[(int)e];
550 			accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
551 			    fpchip_state.status;
552 			ap = __fex_accrued();
553 			accrued |= *ap;
554 			accrued &= 0x3d;
555 			__fex_mklog(uap, (char *)addr, accrued, e, mode,
556 			    (void *)handler);
557 
558 			if (mode == FEX_NOHANDLER) {
559 				__fenv_setcwsw(&oldcwsw);
560 				__fenv_setmxcsr(&oldmxcsr);
561 				goto not_ieee;
562 			} else if (mode == FEX_ABORT) {
563 				abort();
564 			} else if (mode == FEX_SIGNAL) {
565 				__fenv_setcwsw(&oldcwsw);
566 				__fenv_setmxcsr(&oldmxcsr);
567 				handler(sig, &osip, uap);
568 				return;
569 			} else if (mode == FEX_CUSTOM) {
570 				*ap = 0;
571 				if (addr >= (unsigned long)feraiseexcept &&
572 				    addr < (unsigned long)fetestexcept) {
573 					info.op = fex_other;
574 					info.op1.type = info.op2.type =
575 					    info.res.type = fex_nodata;
576 				}
577 				handler(1 << (int)e, &info);
578 				__fenv_setcwsw(&cwsw);
579 				__fenv_setmxcsr(&mxcsr);
580 			}
581 
582 			__fex_st_sse_result(uap, &inst, e, &info);
583 			accrued |= info.flags;
584 
585 #if defined(__amd64)
586 			/*
587 			 * In 64-bit mode, the 32-bit convert-to-integer
588 			 * instructions zero the upper 32 bits of the
589 			 * destination.  (We do this here and not in
590 			 * __fex_st_sse_result because __fex_st_sse_result
591 			 * can be called from __fex_st_simd_result, too.)
592 			 */
593 			if (inst.op == cvtss2si || inst.op == cvttss2si ||
594 			    inst.op == cvtsd2si || inst.op == cvttsd2si)
595 				inst.op1->i[1] = 0;
596 #endif
597 		}
598 
599 		/* advance the pc past the SSE instruction */
600 		uap->uc_mcontext.gregs[REG_PC] += len;
601 		goto update_state;
602 	}
603 
604 	/* determine which exception occurred */
605 	__fex_get_x86_exc(sip, uap);
606 	switch (sip->si_code) {
607 	case FPE_FLTDIV:
608 		e = fex_division;
609 		break;
610 	case FPE_FLTOVF:
611 		e = fex_overflow;
612 		break;
613 	case FPE_FLTUND:
614 		e = fex_underflow;
615 		break;
616 	case FPE_FLTRES:
617 		e = fex_inexact;
618 		break;
619 	case FPE_FLTINV:
620 		if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
621 			goto not_ieee;
622 		break;
623 	default:
624 		/* not an IEEE exception */
625 		goto not_ieee;
626 	}
627 
628 	/* get the handling mode */
629 	mode = FEX_NOHANDLER;
630 	handler = oact.sa_handler; /* for log; just looking, no need to lock */
631 	thr_handlers = __fex_get_thr_handlers();
632 	if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
633 		mode = thr_handlers[(int)e].__mode;
634 		handler = thr_handlers[(int)e].__handler;
635 	}
636 
637 	/* make an entry in the log of retro. diag. if need be */
638 #if defined(__amd64)
639 	addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
640 	    fpchip_state.rip;
641 #else
642 	addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
643 	    fpchip_state.state[3];
644 #endif
645 	accrued = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status &
646 	    ~te_bit[(int)e];
647 	if (test_sse_hw)
648 		accrued |= uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
649 		    mxcsr;
650 	ap = __fex_accrued();
651 	accrued |= *ap;
652 	accrued &= 0x3d;
653 	__fex_mklog(uap, (char *)addr, accrued, e, mode, (void *)handler);
654 
655 	/* handle the exception based on the mode */
656 	if (mode == FEX_NOHANDLER)
657 		goto not_ieee;
658 	else if (mode == FEX_ABORT)
659 		abort();
660 	else if (mode == FEX_SIGNAL) {
661 		handler(sig, &osip, uap);
662 		return;
663 	}
664 
665 	/* disable all traps and clear flags */
666 	__fenv_getcwsw(&cwsw);
667 	cwsw = (cwsw & ~0x3f) | 0x003f0000;
668 	__fenv_setcwsw(&cwsw);
669 	if (test_sse_hw) {
670 		__fenv_getmxcsr(&mxcsr);
671 		mxcsr = (mxcsr & ~0x3f) | 0x1f80;
672 		__fenv_setmxcsr(&mxcsr);
673 	}
674 	*ap = 0;
675 
676 	/* decode the operation */
677 	__fex_get_op(sip, uap, &info);
678 
679 	/* if a custom mode handler is installed, invoke it */
680 	if (mode == FEX_CUSTOM) {
681 		/* if we got here from feraiseexcept, pass dummy info */
682 		if (addr >= (unsigned long)feraiseexcept &&
683 		    addr < (unsigned long)fetestexcept) {
684 			info.op = fex_other;
685 			info.op1.type = info.op2.type = info.res.type =
686 			    fex_nodata;
687 		}
688 
689 		handler(1 << (int)e, &info);
690 
691 		/* restore modes in case the user's handler changed them */
692 		__fenv_setcwsw(&cwsw);
693 		if (test_sse_hw)
694 			__fenv_setmxcsr(&mxcsr);
695 	}
696 
697 	/* stuff the result */
698 	__fex_st_result(sip, uap, &info);
699 	accrued |= info.flags;
700 
701 update_state:
702 	accrued &= 0x3d;
703 	i = __fex_te_needed(thr_handlers, accrued);
704 	*ap = accrued & i;
705 #if defined(__amd64)
706 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw &= ~0x3d;
707 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= (accrued & ~i);
708 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw |= 0x3d;
709 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw &= ~i;
710 #else
711 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] &= ~0x3d;
712 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] |=
713 	    (accrued & ~i);
714 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] |= 0x3d;
715 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] &= ~i;
716 #endif
717 	if (test_sse_hw) {
718 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~0x3d;
719 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr |=
720 		    0x1e80 | (accrued & ~i);
721 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &=
722 		    ~(i << 7);
723 	}
724 	return;
725 
726 not_ieee:
727 	/* revert to the saved handler (if any) */
728 	mutex_lock(&hdlr_lock);
729 	act = oact;
730 	mutex_unlock(&hdlr_lock);
731 	switch ((unsigned long)act.sa_handler) {
732 	case (unsigned long)SIG_DFL:
733 		/* simulate trap with no handler installed */
734 		sigaction(SIGFPE, &act, NULL);
735 		kill(getpid(), SIGFPE);
736 		break;
737 #if !defined(__lint)
738 	case (unsigned long)SIG_IGN:
739 		break;
740 #endif
741 	default:
742 		act.sa_handler(sig, &osip, uap);
743 	}
744 }
745 
746 #else
747 #error Unknown architecture
748 #endif
749 
750 /*
751 *  Return a pointer to the thread-specific handler data, and
752 *  initialize it if necessary
753 */
754 struct fex_handler_data *
755 __fex_get_thr_handlers()
756 {
757 	struct fex_handler_data	*ptr;
758 	unsigned long			fsr;
759 	int						i, te;
760 
761 	if (thr_main()) {
762 		if (!handlers_initialized) {
763 			/* initialize to FEX_NOHANDLER if trap is enabled,
764 			   FEX_NONSTOP if trap is disabled */
765 			__fenv_getfsr(&fsr);
766 			te = (int)__fenv_get_te(fsr);
767 			for (i = 0; i < FEX_NUM_EXC; i++)
768 				main_handlers[i].__mode =
769 					((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
770 			handlers_initialized = 1;
771 		}
772 		return main_handlers;
773 	}
774 	else {
775 		ptr = NULL;
776 		mutex_lock(&handlers_key_lock);
777 		if (thr_getspecific(handlers_key, (void **)&ptr) != 0 &&
778 			thr_keycreate(&handlers_key, free) != 0) {
779 			mutex_unlock(&handlers_key_lock);
780 			return NULL;
781 		}
782 		mutex_unlock(&handlers_key_lock);
783 		if (!ptr) {
784 			if ((ptr = (struct fex_handler_data *)
785 				malloc(sizeof(fex_handler_t))) == NULL) {
786 				return NULL;
787 			}
788 			if (thr_setspecific(handlers_key, (void *)ptr) != 0) {
789 				(void)free(ptr);
790 				return NULL;
791 			}
792 			/* initialize to FEX_NOHANDLER if trap is enabled,
793 			   FEX_NONSTOP if trap is disabled */
794 			__fenv_getfsr(&fsr);
795 			te = (int)__fenv_get_te(fsr);
796 			for (i = 0; i < FEX_NUM_EXC; i++)
797 				ptr[i].__mode = ((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
798 		}
799 		return ptr;
800 	}
801 }
802 
803 /*
804 *  Update the trap enable bits according to the selected modes
805 */
806 void
807 __fex_update_te()
808 {
809 	struct fex_handler_data	*thr_handlers;
810 	struct sigaction		act, tmpact;
811 	sigset_t				blocked;
812 	unsigned long			fsr;
813 	int						te;
814 
815 	/* determine which traps are needed */
816 	thr_handlers = __fex_get_thr_handlers();
817 	__fenv_getfsr(&fsr);
818 	te = __fex_te_needed(thr_handlers, fsr);
819 
820 	/* install __fex_hdlr as necessary */
821 	if (!hdlr_installed && te) {
822 		act.sa_handler = __fex_hdlr;
823 		sigemptyset(&act.sa_mask);
824 		act.sa_flags = SA_SIGINFO;
825 		sigaction(SIGFPE, &act, &tmpact);
826 		if (tmpact.sa_handler != __fex_hdlr)
827 		{
828 			mutex_lock(&hdlr_lock);
829 			oact = tmpact;
830 			mutex_unlock(&hdlr_lock);
831 		}
832 		hdlr_installed = 1;
833 	}
834 
835 	/* set the new trap enable bits (only if SIGFPE is not blocked) */
836 	if (sigprocmask(0, NULL, &blocked) == 0 &&
837 		!sigismember(&blocked, SIGFPE)) {
838 		__fenv_set_te(fsr, te);
839 		__fenv_setfsr(&fsr);
840 	}
841 
842 	/* synchronize with libmtsk */
843 	__mt_fex_sync = __fex_sync_with_libmtsk;
844 
845 #ifdef LIBM_MT_FEX_SYNC
846 	/* synchronize with other projects */
847 	__libm_mt_fex_sync = __fex_sync_with_threads;
848 #endif
849 }
850