__fex_hdlr.c revision 25c28e83beb90e7c80452a7c818c5e6f73a07dc8
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "fenv_synonyms.h"
#include <signal.h>
#include <siginfo.h>
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread.h>
#include <math.h>
#if defined(__SUNPRO_C)
#include <sunmath.h>
#endif
#include <fenv.h>
#include "fex_handler.h"
#include "fenv_inlines.h"
#endif
/* 2.x signal.h doesn't declare sigemptyset or sigismember
extern int sigemptyset(sigset_t *);
extern int sigismember(const sigset_t *, int);
/* external globals */
#pragma weak __mt_fex_sync
#ifdef LIBM_MT_FEX_SYNC
#pragma weak __libm_mt_fex_sync
#endif
/* private variables */
static fex_handler_t main_handlers;
static int handlers_initialized = 0;
static thread_key_t handlers_key;
static int hdlr_installed = 0;
/* private const data */
static const int te_bit[FEX_NUM_EXC] = {
1 << fp_trap_inexact,
1 << fp_trap_division,
1 << fp_trap_underflow,
1 << fp_trap_overflow,
1 << fp_trap_invalid,
1 << fp_trap_invalid,
1 << fp_trap_invalid,
1 << fp_trap_invalid,
1 << fp_trap_invalid,
1 << fp_trap_invalid,
1 << fp_trap_invalid,
1 << fp_trap_invalid
};
/*
* Return the traps to be enabled given the current handling modes
* and flags
*/
static int
{
/* set traps for handling modes */
te = 0;
for (i = 0; i < FEX_NUM_EXC; i++)
/* add traps for retrospective diagnostics */
if (fex_get_log()) {
if (!(ex & FE_INEXACT))
if (!(ex & FE_UNDERFLOW))
if (!(ex & FE_OVERFLOW))
if (!(ex & FE_DIVBYZERO))
if (!(ex & FE_INVALID))
}
return te;
}
/*
* The following function synchronizes with libmtsk (SPARC only, for now)
*/
static void
{
static fenv_t master_env;
static int env_initialized = 0;
if (begin) {
if (master) {
(void) fegetenv(&master_env);
env_initialized = 1;
}
else if (env_initialized)
(void) fesetenv(&master_env);
}
else if (master && fex_get_log())
}
#ifdef LIBM_MT_FEX_SYNC
/*
* The following function may be used for synchronization with any
* internal project that manages multiple threads
*/
enum __libm_mt_fex_sync_actions {
};
struct __libm_mt_fex_sync_data {
int initialized;
};
static void
struct __libm_mt_fex_sync_data *thr_env)
{
switch (action) {
break;
if (thr_env->initialized)
break;
#if defined(__x86)
#else
if (fex_get_log())
#endif
break;
#if defined(__x86)
/* clear traps, making all accrued flags visible in status word */
{
unsigned long fsr;
__fenv_getfsr(&fsr);
__fenv_set_te(fsr, 0);
__fenv_setfsr(&fsr);
}
#endif
break;
}
}
#endif
#if defined(__sparc)
/*
* Code for setting or clearing interval mode on US-III and above.
* This is embedded as data so we don't have to mark the library
* modified the second word to set the bits I want, but that would
* have required another mutex.)
*/
static const unsigned int siam[][2] = {
{ 0x81c3e008, 0x81b01020 }, /* retl, siam 0 */
{ 0x81c3e008, 0x81b01024 }, /* retl, siam 4 */
{ 0x81c3e008, 0x81b01025 }, /* retl, siam 5 */
{ 0x81c3e008, 0x81b01026 }, /* retl, siam 6 */
{ 0x81c3e008, 0x81b01027 } /* retl, siam 7 */
};
/*
* If a handling mode is in effect, apply it; otherwise invoke the
* saved handler
*/
static void
{
struct fex_handler_data *thr_handlers;
int mode, i;
enum fex_exception e;
unsigned int gsr;
/* determine which exception occurred */
case FPE_FLTDIV:
e = fex_division;
break;
case FPE_FLTOVF:
e = fex_overflow;
break;
case FPE_FLTUND:
e = fex_underflow;
break;
case FPE_FLTRES:
e = fex_inexact;
break;
case FPE_FLTINV:
goto not_ieee;
break;
default:
/* not an IEEE exception */
goto not_ieee;
}
/* get the handling mode */
}
/* make an entry in the log of retro. diag. if need be */
/* handle the exception based on the mode */
if (mode == FEX_NOHANDLER)
goto not_ieee;
abort();
else if (mode == FEX_SIGNAL) {
return;
}
/* custom or nonstop mode; disable traps and clear flags */
__fenv_getfsr(&fsr);
__fenv_set_te(fsr, 0);
__fenv_set_ex(fsr, 0);
/* if interval mode was set, clear it, then substitute the
interval rounding direction and clear ns mode in the fsr */
#ifdef __sparcv9
#else
gsr = 0;
#endif
if (gsr & 4) {
siamp();
}
__fenv_setfsr(&fsr);
/* decode the operation */
/* if a custom mode handler is installed, invoke it */
if (mode == FEX_CUSTOM) {
/* if we got here from feraiseexcept, pass dummy info */
if (addr >= (unsigned long)feraiseexcept &&
addr < (unsigned long)fetestexcept) {
}
/* restore interval mode if it was set, and put the original
rounding direction and ns mode back in the fsr */
if (gsr & 4) {
siamp();
}
/* restore modes in case the user's handler changed them */
if (gsr & 4) {
siamp();
}
__fenv_setfsr(&fsr);
}
/* stuff the result */
/* "or" in any exception flags and update traps */
__fenv_set_te(fsr, i);
return;
/* revert to the saved handler (if any) */
switch ((unsigned long)act.sa_handler) {
case (unsigned long)SIG_DFL:
/* simulate trap with no handler installed */
break;
#if !defined(__lint)
case (unsigned long)SIG_IGN:
break;
#endif
default:
}
}
#if defined(__amd64)
#define test_sse_hw 1
#else
extern int _sse_hw;
#define test_sse_hw _sse_hw
#endif
#if !defined(REG_PC)
#endif
/*
* If a handling mode is in effect, apply it; otherwise invoke the
* saved handler
*/
static void
{
struct fex_handler_data *thr_handlers;
unsigned long addr;
/* check for an exception caused by an SSE instruction */
if (len == 0)
goto not_ieee;
/* disable all traps and clear flags */
e = (enum fex_exception)-1;
mode = FEX_NONSTOP;
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
e = simd_e[i];
simd_mode[i] = FEX_NOHANDLER;
if (thr_handlers &&
thr_handlers[(int)e].__mode !=
simd_mode[i] =
thr_handlers[(int)e].__mode;
simd_handler[i] =
thr_handlers[(int)e].__handler;
}
switch (simd_mode[i]) {
case FEX_ABORT:
break;
case FEX_SIGNAL:
mode = FEX_SIGNAL;
handler = simd_handler[i];
break;
case FEX_NOHANDLER:
break;
}
}
if (e == (enum fex_exception)-1) {
goto not_ieee;
}
ap = __fex_accrued();
accrued &= 0x3d;
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
(void *)simd_handler[i]);
}
if (mode == FEX_NOHANDLER) {
goto not_ieee;
abort();
} else if (mode == FEX_SIGNAL) {
return;
}
*ap = 0;
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
if (simd_mode[i] == FEX_CUSTOM) {
&simd_info[i]);
}
}
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
}
/* set MMX mode */
#if defined(__amd64)
fpchip_state.fctw = 0;
#else
#endif
}
} else {
if ((int)e < 0) {
goto not_ieee;
}
}
ap = __fex_accrued();
accrued &= 0x3d;
(void *)handler);
if (mode == FEX_NOHANDLER) {
goto not_ieee;
abort();
} else if (mode == FEX_SIGNAL) {
return;
} else if (mode == FEX_CUSTOM) {
*ap = 0;
if (addr >= (unsigned long)feraiseexcept &&
addr < (unsigned long)fetestexcept) {
}
}
#if defined(__amd64)
/*
* In 64-bit mode, the 32-bit convert-to-integer
* instructions zero the upper 32 bits of the
* destination. (We do this here and not in
* __fex_st_sse_result because __fex_st_sse_result
* can be called from __fex_st_simd_result, too.)
*/
#endif
}
/* advance the pc past the SSE instruction */
goto update_state;
}
/* determine which exception occurred */
case FPE_FLTDIV:
e = fex_division;
break;
case FPE_FLTOVF:
e = fex_overflow;
break;
case FPE_FLTUND:
e = fex_underflow;
break;
case FPE_FLTRES:
e = fex_inexact;
break;
case FPE_FLTINV:
goto not_ieee;
break;
default:
/* not an IEEE exception */
goto not_ieee;
}
/* get the handling mode */
}
/* make an entry in the log of retro. diag. if need be */
#if defined(__amd64)
#else
#endif
~te_bit[(int)e];
if (test_sse_hw)
ap = __fex_accrued();
accrued &= 0x3d;
/* handle the exception based on the mode */
if (mode == FEX_NOHANDLER)
goto not_ieee;
abort();
else if (mode == FEX_SIGNAL) {
return;
}
/* disable all traps and clear flags */
if (test_sse_hw) {
}
*ap = 0;
/* decode the operation */
/* if a custom mode handler is installed, invoke it */
if (mode == FEX_CUSTOM) {
/* if we got here from feraiseexcept, pass dummy info */
if (addr >= (unsigned long)feraiseexcept &&
addr < (unsigned long)fetestexcept) {
}
/* restore modes in case the user's handler changed them */
if (test_sse_hw)
}
/* stuff the result */
accrued &= 0x3d;
#if defined(__amd64)
#else
(accrued & ~i);
#endif
if (test_sse_hw) {
0x1e80 | (accrued & ~i);
~(i << 7);
}
return;
/* revert to the saved handler (if any) */
switch ((unsigned long)act.sa_handler) {
case (unsigned long)SIG_DFL:
/* simulate trap with no handler installed */
break;
#if !defined(__lint)
case (unsigned long)SIG_IGN:
break;
#endif
default:
}
}
#else
#endif
/*
* Return a pointer to the thread-specific handler data, and
* initialize it if necessary
*/
struct fex_handler_data *
{
struct fex_handler_data *ptr;
unsigned long fsr;
int i, te;
if (thr_main()) {
if (!handlers_initialized) {
/* initialize to FEX_NOHANDLER if trap is enabled,
FEX_NONSTOP if trap is disabled */
__fenv_getfsr(&fsr);
for (i = 0; i < FEX_NUM_EXC; i++)
main_handlers[i].__mode =
handlers_initialized = 1;
}
return main_handlers;
}
else {
return NULL;
}
if (!ptr) {
if ((ptr = (struct fex_handler_data *)
return NULL;
}
return NULL;
}
/* initialize to FEX_NOHANDLER if trap is enabled,
FEX_NONSTOP if trap is disabled */
__fenv_getfsr(&fsr);
for (i = 0; i < FEX_NUM_EXC; i++)
}
return ptr;
}
}
/*
* Update the trap enable bits according to the selected modes
*/
void
{
struct fex_handler_data *thr_handlers;
unsigned long fsr;
int te;
/* determine which traps are needed */
__fenv_getfsr(&fsr);
/* install __fex_hdlr as necessary */
if (!hdlr_installed && te) {
{
}
hdlr_installed = 1;
}
/* set the new trap enable bits (only if SIGFPE is not blocked) */
__fenv_setfsr(&fsr);
}
/* synchronize with libmtsk */
#ifdef LIBM_MT_FEX_SYNC
/* synchronize with other projects */
#endif
}