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