xref: /illumos-gate/usr/src/lib/libm/common/m9x/__fex_hdlr.c (revision 0250c53ad267726f2438e3c6556199a0bbf588a2)
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, void *arg)
227 {
228 	ucontext_t		*uap = arg;
229 	struct fex_handler_data	*thr_handlers;
230 	struct sigaction	act;
231 	void			(*handler)(), (*siamp)();
232 	int			mode, i;
233 	enum fex_exception	e;
234 	fex_info_t		info;
235 	unsigned long		fsr, tmpfsr, addr;
236 	unsigned int		gsr;
237 
238 	/* determine which exception occurred */
239 	switch (sip->si_code) {
240 	case FPE_FLTDIV:
241 		e = fex_division;
242 		break;
243 	case FPE_FLTOVF:
244 		e = fex_overflow;
245 		break;
246 	case FPE_FLTUND:
247 		e = fex_underflow;
248 		break;
249 	case FPE_FLTRES:
250 		e = fex_inexact;
251 		break;
252 	case FPE_FLTINV:
253 		if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
254 			goto not_ieee;
255 		break;
256 	default:
257 		/* not an IEEE exception */
258 		goto not_ieee;
259 	}
260 
261 	/* get the handling mode */
262 	mode = FEX_NOHANDLER;
263 	handler = oact.sa_handler; /* for log; just looking, no need to lock */
264 	thr_handlers = __fex_get_thr_handlers();
265 	if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
266 		mode = thr_handlers[(int)e].__mode;
267 		handler = thr_handlers[(int)e].__handler;
268 	}
269 
270 	/* make an entry in the log of retro. diag. if need be */
271 	i = ((int)uap->uc_mcontext.fpregs.fpu_fsr >> 5) & 0x1f;
272 	__fex_mklog(uap, (char *)sip->si_addr, i, e, mode, (void *)handler);
273 
274 	/* handle the exception based on the mode */
275 	if (mode == FEX_NOHANDLER)
276 		goto not_ieee;
277 	else if (mode == FEX_ABORT)
278 		abort();
279 	else if (mode == FEX_SIGNAL) {
280 		handler(sig, sip, uap);
281 		return;
282 	}
283 
284 	/* custom or nonstop mode; disable traps and clear flags */
285 	__fenv_getfsr(&fsr);
286 	__fenv_set_te(fsr, 0);
287 	__fenv_set_ex(fsr, 0);
288 
289 	/* if interval mode was set, clear it, then substitute the
290 	   interval rounding direction and clear ns mode in the fsr */
291 #ifdef __sparcv9
292 	gsr = uap->uc_mcontext.asrs[3];
293 #else
294 	gsr = 0;
295 	if (uap->uc_mcontext.xrs.xrs_id == XRS_ID)
296 		gsr = (*(unsigned long long*)((prxregset_t*)uap->uc_mcontext.
297 		    xrs.xrs_ptr)->pr_un.pr_v8p.pr_filler);
298 #endif
299 	gsr = (gsr >> 25) & 7;
300 	if (gsr & 4) {
301 		siamp = (void (*)()) siam[0];
302 		siamp();
303 		tmpfsr = fsr;
304 		fsr = (fsr & ~0xc0400000ul) | ((gsr & 3) << 30);
305 	}
306 	__fenv_setfsr(&fsr);
307 
308 	/* decode the operation */
309 	__fex_get_op(sip, uap, &info);
310 
311 	/* if a custom mode handler is installed, invoke it */
312 	if (mode == FEX_CUSTOM) {
313 		/* if we got here from feraiseexcept, pass dummy info */
314 		addr = (unsigned long)sip->si_addr;
315 		if (addr >= (unsigned long)feraiseexcept &&
316 		    addr < (unsigned long)fetestexcept) {
317 			info.op = fex_other;
318 			info.op1.type = info.op2.type = info.res.type =
319 			    fex_nodata;
320 		}
321 
322 		/* restore interval mode if it was set, and put the original
323 		   rounding direction and ns mode back in the fsr */
324 		if (gsr & 4) {
325 			__fenv_setfsr(&tmpfsr);
326 			siamp = (void (*)()) siam[1 + (gsr & 3)];
327 			siamp();
328 		}
329 
330 		handler(1 << (int)e, &info);
331 
332 		/* restore modes in case the user's handler changed them */
333 		if (gsr & 4) {
334 			siamp = (void (*)()) siam[0];
335 			siamp();
336 		}
337 		__fenv_setfsr(&fsr);
338 	}
339 
340 	/* stuff the result */
341 	__fex_st_result(sip, uap, &info);
342 
343 	/* "or" in any exception flags and update traps */
344 	fsr = uap->uc_mcontext.fpregs.fpu_fsr;
345 	fsr |= ((info.flags & 0x1f) << 5);
346 	i = __fex_te_needed(thr_handlers, fsr);
347 	__fenv_set_te(fsr, i);
348 	uap->uc_mcontext.fpregs.fpu_fsr = fsr;
349 	return;
350 
351 not_ieee:
352 	/* revert to the saved handler (if any) */
353 	mutex_lock(&hdlr_lock);
354 	act = oact;
355 	mutex_unlock(&hdlr_lock);
356 	switch ((unsigned long)act.sa_handler) {
357 	case (unsigned long)SIG_DFL:
358 		/* simulate trap with no handler installed */
359 		sigaction(SIGFPE, &act, NULL);
360 		kill(getpid(), SIGFPE);
361 		break;
362 #if !defined(__lint)
363 	case (unsigned long)SIG_IGN:
364 		break;
365 #endif
366 	default:
367 		act.sa_handler(sig, sip, uap);
368 	}
369 }
370 
371 #elif defined(__x86)
372 
373 #if defined(__amd64)
374 #define test_sse_hw	1
375 #else
376 extern int _sse_hw;
377 #define test_sse_hw	_sse_hw
378 #endif
379 
380 #if !defined(REG_PC)
381 #define REG_PC	EIP
382 #endif
383 
384 /*
385 *  If a handling mode is in effect, apply it; otherwise invoke the
386 *  saved handler
387 */
388 static void
389 __fex_hdlr(int sig, siginfo_t *sip, void *arg)
390 {
391 	ucontext_t		*uap = arg;
392 	struct fex_handler_data	*thr_handlers;
393 	struct sigaction	act;
394 	void			(*handler)() = NULL, (*simd_handler[4])();
395 	int			mode, simd_mode[4], i, len, accrued, *ap;
396 	unsigned int		cwsw, oldcwsw, mxcsr, oldmxcsr;
397 	enum fex_exception	e, simd_e[4];
398 	fex_info_t		info, simd_info[4];
399 	unsigned long		addr;
400 	siginfo_t		osip = *sip;
401 	sseinst_t		inst;
402 
403 	/* check for an exception caused by an SSE instruction */
404 	if (!(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 0x80)) {
405 		len = __fex_parse_sse(uap, &inst);
406 		if (len == 0)
407 			goto not_ieee;
408 
409 		/* disable all traps and clear flags */
410 		__fenv_getcwsw(&oldcwsw);
411 		cwsw = (oldcwsw & ~0x3f) | 0x003f0000;
412 		__fenv_setcwsw(&cwsw);
413 		__fenv_getmxcsr(&oldmxcsr);
414 		mxcsr = (oldmxcsr & ~0x3f) | 0x1f80;
415 		__fenv_setmxcsr(&mxcsr);
416 
417 		if ((int)inst.op & SIMD) {
418 			__fex_get_simd_op(uap, &inst, simd_e, simd_info);
419 
420 			thr_handlers = __fex_get_thr_handlers();
421 			addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
422 			accrued = uap->uc_mcontext.fpregs.fp_reg_set.
423 			    fpchip_state.mxcsr;
424 
425 			e = (enum fex_exception)-1;
426 			mode = FEX_NONSTOP;
427 			for (i = 0; i < 4; i++) {
428 				if ((int)simd_e[i] < 0)
429 					continue;
430 
431 				e = simd_e[i];
432 				simd_mode[i] = FEX_NOHANDLER;
433 				simd_handler[i] = oact.sa_handler;
434 				if (thr_handlers &&
435 				    thr_handlers[(int)e].__mode !=
436 				    FEX_NOHANDLER) {
437 					simd_mode[i] =
438 					    thr_handlers[(int)e].__mode;
439 					simd_handler[i] =
440 					    thr_handlers[(int)e].__handler;
441 				}
442 				accrued &= ~te_bit[(int)e];
443 				switch (simd_mode[i]) {
444 				case FEX_ABORT:
445 					mode = FEX_ABORT;
446 					break;
447 				case FEX_SIGNAL:
448 					if (mode != FEX_ABORT)
449 						mode = FEX_SIGNAL;
450 					handler = simd_handler[i];
451 					break;
452 				case FEX_NOHANDLER:
453 					if (mode != FEX_ABORT && mode !=
454 					    FEX_SIGNAL)
455 						mode = FEX_NOHANDLER;
456 					break;
457 				}
458 			}
459 			if (e == (enum fex_exception)-1) {
460 				__fenv_setcwsw(&oldcwsw);
461 				__fenv_setmxcsr(&oldmxcsr);
462 				goto not_ieee;
463 			}
464 			accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
465 			    fpchip_state.status;
466 			ap = __fex_accrued();
467 			accrued |= *ap;
468 			accrued &= 0x3d;
469 
470 			for (i = 0; i < 4; i++) {
471 				if ((int)simd_e[i] < 0)
472 					continue;
473 
474 				__fex_mklog(uap, (char *)addr, accrued,
475 				    simd_e[i], simd_mode[i],
476 				    (void *)simd_handler[i]);
477 			}
478 
479 			if (mode == FEX_NOHANDLER) {
480 				__fenv_setcwsw(&oldcwsw);
481 				__fenv_setmxcsr(&oldmxcsr);
482 				goto not_ieee;
483 			} else if (mode == FEX_ABORT) {
484 				abort();
485 			} else if (mode == FEX_SIGNAL) {
486 				__fenv_setcwsw(&oldcwsw);
487 				__fenv_setmxcsr(&oldmxcsr);
488 				handler(sig, &osip, uap);
489 				return;
490 			}
491 
492 			*ap = 0;
493 			for (i = 0; i < 4; i++) {
494 				if ((int)simd_e[i] < 0)
495 					continue;
496 
497 				if (simd_mode[i] == FEX_CUSTOM) {
498 					handler(1 << (int)simd_e[i],
499 					    &simd_info[i]);
500 					__fenv_setcwsw(&cwsw);
501 					__fenv_setmxcsr(&mxcsr);
502 				}
503 			}
504 
505 			__fex_st_simd_result(uap, &inst, simd_e, simd_info);
506 			for (i = 0; i < 4; i++) {
507 				if ((int)simd_e[i] < 0)
508 					continue;
509 
510 				accrued |= simd_info[i].flags;
511 			}
512 
513 			if ((int)inst.op & INTREG) {
514 				/* set MMX mode */
515 #if defined(__amd64)
516 				uap->uc_mcontext.fpregs.fp_reg_set.
517 				    fpchip_state.sw &= ~0x3800;
518 				uap->uc_mcontext.fpregs.fp_reg_set.
519 				    fpchip_state.fctw = 0;
520 #else
521 				uap->uc_mcontext.fpregs.fp_reg_set.
522 				    fpchip_state.state[1] &= ~0x3800;
523 				uap->uc_mcontext.fpregs.fp_reg_set.
524 				    fpchip_state.state[2] = 0;
525 #endif
526 			}
527 		} else {
528 			e = __fex_get_sse_op(uap, &inst, &info);
529 			if ((int)e < 0) {
530 				__fenv_setcwsw(&oldcwsw);
531 				__fenv_setmxcsr(&oldmxcsr);
532 				goto not_ieee;
533 			}
534 
535 			mode = FEX_NOHANDLER;
536 			handler = oact.sa_handler;
537 			thr_handlers = __fex_get_thr_handlers();
538 			if (thr_handlers && thr_handlers[(int)e].__mode !=
539 			    FEX_NOHANDLER) {
540 				mode = thr_handlers[(int)e].__mode;
541 				handler = thr_handlers[(int)e].__handler;
542 			}
543 
544 			addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
545 			accrued = uap->uc_mcontext.fpregs.fp_reg_set.
546 			    fpchip_state.mxcsr & ~te_bit[(int)e];
547 			accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
548 			    fpchip_state.status;
549 			ap = __fex_accrued();
550 			accrued |= *ap;
551 			accrued &= 0x3d;
552 			__fex_mklog(uap, (char *)addr, accrued, e, mode,
553 			    (void *)handler);
554 
555 			if (mode == FEX_NOHANDLER) {
556 				__fenv_setcwsw(&oldcwsw);
557 				__fenv_setmxcsr(&oldmxcsr);
558 				goto not_ieee;
559 			} else if (mode == FEX_ABORT) {
560 				abort();
561 			} else if (mode == FEX_SIGNAL) {
562 				__fenv_setcwsw(&oldcwsw);
563 				__fenv_setmxcsr(&oldmxcsr);
564 				handler(sig, &osip, uap);
565 				return;
566 			} else if (mode == FEX_CUSTOM) {
567 				*ap = 0;
568 				if (addr >= (unsigned long)feraiseexcept &&
569 				    addr < (unsigned long)fetestexcept) {
570 					info.op = fex_other;
571 					info.op1.type = info.op2.type =
572 					    info.res.type = fex_nodata;
573 				}
574 				handler(1 << (int)e, &info);
575 				__fenv_setcwsw(&cwsw);
576 				__fenv_setmxcsr(&mxcsr);
577 			}
578 
579 			__fex_st_sse_result(uap, &inst, e, &info);
580 			accrued |= info.flags;
581 
582 #if defined(__amd64)
583 			/*
584 			 * In 64-bit mode, the 32-bit convert-to-integer
585 			 * instructions zero the upper 32 bits of the
586 			 * destination.  (We do this here and not in
587 			 * __fex_st_sse_result because __fex_st_sse_result
588 			 * can be called from __fex_st_simd_result, too.)
589 			 */
590 			if (inst.op == cvtss2si || inst.op == cvttss2si ||
591 			    inst.op == cvtsd2si || inst.op == cvttsd2si)
592 				inst.op1->i[1] = 0;
593 #endif
594 		}
595 
596 		/* advance the pc past the SSE instruction */
597 		uap->uc_mcontext.gregs[REG_PC] += len;
598 		goto update_state;
599 	}
600 
601 	/* determine which exception occurred */
602 	__fex_get_x86_exc(sip, uap);
603 	switch (sip->si_code) {
604 	case FPE_FLTDIV:
605 		e = fex_division;
606 		break;
607 	case FPE_FLTOVF:
608 		e = fex_overflow;
609 		break;
610 	case FPE_FLTUND:
611 		e = fex_underflow;
612 		break;
613 	case FPE_FLTRES:
614 		e = fex_inexact;
615 		break;
616 	case FPE_FLTINV:
617 		if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
618 			goto not_ieee;
619 		break;
620 	default:
621 		/* not an IEEE exception */
622 		goto not_ieee;
623 	}
624 
625 	/* get the handling mode */
626 	mode = FEX_NOHANDLER;
627 	handler = oact.sa_handler; /* for log; just looking, no need to lock */
628 	thr_handlers = __fex_get_thr_handlers();
629 	if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
630 		mode = thr_handlers[(int)e].__mode;
631 		handler = thr_handlers[(int)e].__handler;
632 	}
633 
634 	/* make an entry in the log of retro. diag. if need be */
635 #if defined(__amd64)
636 	addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
637 	    fpchip_state.rip;
638 #else
639 	addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
640 	    fpchip_state.state[3];
641 #endif
642 	accrued = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status &
643 	    ~te_bit[(int)e];
644 	if (test_sse_hw)
645 		accrued |= uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
646 		    mxcsr;
647 	ap = __fex_accrued();
648 	accrued |= *ap;
649 	accrued &= 0x3d;
650 	__fex_mklog(uap, (char *)addr, accrued, e, mode, (void *)handler);
651 
652 	/* handle the exception based on the mode */
653 	if (mode == FEX_NOHANDLER)
654 		goto not_ieee;
655 	else if (mode == FEX_ABORT)
656 		abort();
657 	else if (mode == FEX_SIGNAL) {
658 		handler(sig, &osip, uap);
659 		return;
660 	}
661 
662 	/* disable all traps and clear flags */
663 	__fenv_getcwsw(&cwsw);
664 	cwsw = (cwsw & ~0x3f) | 0x003f0000;
665 	__fenv_setcwsw(&cwsw);
666 	if (test_sse_hw) {
667 		__fenv_getmxcsr(&mxcsr);
668 		mxcsr = (mxcsr & ~0x3f) | 0x1f80;
669 		__fenv_setmxcsr(&mxcsr);
670 	}
671 	*ap = 0;
672 
673 	/* decode the operation */
674 	__fex_get_op(sip, uap, &info);
675 
676 	/* if a custom mode handler is installed, invoke it */
677 	if (mode == FEX_CUSTOM) {
678 		/* if we got here from feraiseexcept, pass dummy info */
679 		if (addr >= (unsigned long)feraiseexcept &&
680 		    addr < (unsigned long)fetestexcept) {
681 			info.op = fex_other;
682 			info.op1.type = info.op2.type = info.res.type =
683 			    fex_nodata;
684 		}
685 
686 		handler(1 << (int)e, &info);
687 
688 		/* restore modes in case the user's handler changed them */
689 		__fenv_setcwsw(&cwsw);
690 		if (test_sse_hw)
691 			__fenv_setmxcsr(&mxcsr);
692 	}
693 
694 	/* stuff the result */
695 	__fex_st_result(sip, uap, &info);
696 	accrued |= info.flags;
697 
698 update_state:
699 	accrued &= 0x3d;
700 	i = __fex_te_needed(thr_handlers, accrued);
701 	*ap = accrued & i;
702 #if defined(__amd64)
703 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw &= ~0x3d;
704 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= (accrued & ~i);
705 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw |= 0x3d;
706 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw &= ~i;
707 #else
708 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] &= ~0x3d;
709 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] |=
710 	    (accrued & ~i);
711 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] |= 0x3d;
712 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] &= ~i;
713 #endif
714 	if (test_sse_hw) {
715 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~0x3d;
716 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr |=
717 		    0x1e80 | (accrued & ~i);
718 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &=
719 		    ~(i << 7);
720 	}
721 	return;
722 
723 not_ieee:
724 	/* revert to the saved handler (if any) */
725 	mutex_lock(&hdlr_lock);
726 	act = oact;
727 	mutex_unlock(&hdlr_lock);
728 	switch ((unsigned long)act.sa_handler) {
729 	case (unsigned long)SIG_DFL:
730 		/* simulate trap with no handler installed */
731 		sigaction(SIGFPE, &act, NULL);
732 		kill(getpid(), SIGFPE);
733 		break;
734 #if !defined(__lint)
735 	case (unsigned long)SIG_IGN:
736 		break;
737 #endif
738 	default:
739 		act.sa_sigaction(sig, &osip, uap);
740 	}
741 }
742 
743 #else
744 #error Unknown architecture
745 #endif
746 
747 /*
748 *  Return a pointer to the thread-specific handler data, and
749 *  initialize it if necessary
750 */
751 struct fex_handler_data *
752 __fex_get_thr_handlers()
753 {
754 	struct fex_handler_data	*ptr;
755 	unsigned long			fsr;
756 	int						i, te;
757 
758 	if (thr_main()) {
759 		if (!handlers_initialized) {
760 			/* initialize to FEX_NOHANDLER if trap is enabled,
761 			   FEX_NONSTOP if trap is disabled */
762 			__fenv_getfsr(&fsr);
763 			te = (int)__fenv_get_te(fsr);
764 			for (i = 0; i < FEX_NUM_EXC; i++)
765 				main_handlers[i].__mode =
766 					((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
767 			handlers_initialized = 1;
768 		}
769 		return main_handlers;
770 	}
771 	else {
772 		ptr = NULL;
773 		mutex_lock(&handlers_key_lock);
774 		if (thr_getspecific(handlers_key, (void **)&ptr) != 0 &&
775 			thr_keycreate(&handlers_key, free) != 0) {
776 			mutex_unlock(&handlers_key_lock);
777 			return NULL;
778 		}
779 		mutex_unlock(&handlers_key_lock);
780 		if (!ptr) {
781 			if ((ptr = (struct fex_handler_data *)
782 				malloc(sizeof(fex_handler_t))) == NULL) {
783 				return NULL;
784 			}
785 			if (thr_setspecific(handlers_key, (void *)ptr) != 0) {
786 				(void)free(ptr);
787 				return NULL;
788 			}
789 			/* initialize to FEX_NOHANDLER if trap is enabled,
790 			   FEX_NONSTOP if trap is disabled */
791 			__fenv_getfsr(&fsr);
792 			te = (int)__fenv_get_te(fsr);
793 			for (i = 0; i < FEX_NUM_EXC; i++)
794 				ptr[i].__mode = ((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
795 		}
796 		return ptr;
797 	}
798 }
799 
800 /*
801 *  Update the trap enable bits according to the selected modes
802 */
803 void
804 __fex_update_te()
805 {
806 	struct fex_handler_data	*thr_handlers;
807 	struct sigaction		act, tmpact;
808 	sigset_t				blocked;
809 	unsigned long			fsr;
810 	int						te;
811 
812 	/* determine which traps are needed */
813 	thr_handlers = __fex_get_thr_handlers();
814 	__fenv_getfsr(&fsr);
815 	te = __fex_te_needed(thr_handlers, fsr);
816 
817 	/* install __fex_hdlr as necessary */
818 	if (!hdlr_installed && te) {
819 		act.sa_sigaction = __fex_hdlr;
820 		sigemptyset(&act.sa_mask);
821 		act.sa_flags = SA_SIGINFO;
822 		sigaction(SIGFPE, &act, &tmpact);
823 		if (tmpact.sa_sigaction != __fex_hdlr)
824 		{
825 			mutex_lock(&hdlr_lock);
826 			oact = tmpact;
827 			mutex_unlock(&hdlr_lock);
828 		}
829 		hdlr_installed = 1;
830 	}
831 
832 	/* set the new trap enable bits (only if SIGFPE is not blocked) */
833 	if (sigprocmask(0, NULL, &blocked) == 0 &&
834 		!sigismember(&blocked, SIGFPE)) {
835 		__fenv_set_te(fsr, te);
836 		__fenv_setfsr(&fsr);
837 	}
838 
839 	/* synchronize with libmtsk */
840 	__mt_fex_sync = __fex_sync_with_libmtsk;
841 
842 	/* synchronize with other projects */
843 	__libm_mt_fex_sync = __fex_sync_with_threads;
844 }
845