#undef lint
#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"
#if defined(__sparc) && !defined(__sparcv9)
#include <sys/procfs.h>
#endif
extern int sigemptyset(sigset_t *);
extern int sigismember(const sigset_t *, int);
void (*__mt_fex_sync)() = NULL;
#pragma weak __mt_fex_sync
void (*__libm_mt_fex_sync)() = NULL;
#pragma weak __libm_mt_fex_sync
static fex_handler_t main_handlers;
static int handlers_initialized = 0;
static thread_key_t handlers_key;
static mutex_t handlers_key_lock = DEFAULTMUTEX;
static struct sigaction oact = { 0, SIG_DFL };
static mutex_t hdlr_lock = DEFAULTMUTEX;
static int hdlr_installed = 0;
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
};
static int
__fex_te_needed(struct fex_handler_data *thr_handlers, unsigned long fsr)
{
int i, ex, te;
te = 0;
for (i = 0; i < FEX_NUM_EXC; i++)
if (thr_handlers[i].__mode != FEX_NONSTOP)
te |= te_bit[i];
if (fex_get_log()) {
ex = (int)__fenv_get_ex(fsr);
if (!(ex & FE_INEXACT))
te |= (1 << fp_trap_inexact);
if (!(ex & FE_UNDERFLOW))
te |= (1 << fp_trap_underflow);
if (!(ex & FE_OVERFLOW))
te |= (1 << fp_trap_overflow);
if (!(ex & FE_DIVBYZERO))
te |= (1 << fp_trap_division);
if (!(ex & FE_INVALID))
te |= (1 << fp_trap_invalid);
}
return te;
}
static void
__fex_sync_with_libmtsk(int begin, int master)
{
static fenv_t master_env;
static int env_initialized = 0;
static mutex_t env_lock = DEFAULTMUTEX;
if (begin) {
mutex_lock(&env_lock);
if (master) {
(void) fegetenv(&master_env);
env_initialized = 1;
}
else if (env_initialized)
(void) fesetenv(&master_env);
mutex_unlock(&env_lock);
}
else if (master && fex_get_log())
__fex_update_te();
}
enum __libm_mt_fex_sync_actions {
__libm_mt_fex_start_master = 0,
__libm_mt_fex_start_slave,
__libm_mt_fex_finish_master,
__libm_mt_fex_finish_slave
};
struct __libm_mt_fex_sync_data {
fenv_t master_env;
int initialized;
mutex_t lock;
};
static void
__fex_sync_with_threads(enum __libm_mt_fex_sync_actions action,
struct __libm_mt_fex_sync_data *thr_env)
{
switch (action) {
case __libm_mt_fex_start_master:
mutex_lock(&thr_env->lock);
(void) fegetenv(&thr_env->master_env);
thr_env->initialized = 1;
mutex_unlock(&thr_env->lock);
break;
case __libm_mt_fex_start_slave:
mutex_lock(&thr_env->lock);
if (thr_env->initialized)
(void) fesetenv(&thr_env->master_env);
mutex_unlock(&thr_env->lock);
break;
case __libm_mt_fex_finish_master:
#if defined(__x86)
__fex_update_te();
#else
if (fex_get_log())
__fex_update_te();
#endif
break;
case __libm_mt_fex_finish_slave:
#if defined(__x86)
{
unsigned long fsr;
__fenv_getfsr(&fsr);
__fenv_set_te(fsr, 0);
__fenv_setfsr(&fsr);
}
#endif
break;
}
}
#if defined(__sparc)
static const unsigned int siam[][2] = {
{ 0x81c3e008, 0x81b01020 },
{ 0x81c3e008, 0x81b01024 },
{ 0x81c3e008, 0x81b01025 },
{ 0x81c3e008, 0x81b01026 },
{ 0x81c3e008, 0x81b01027 }
};
static void
__fex_hdlr(int sig, siginfo_t *sip, void *arg)
{
ucontext_t *uap = arg;
struct fex_handler_data *thr_handlers;
struct sigaction act;
void (*handler)(), (*siamp)();
int mode, i;
enum fex_exception e;
fex_info_t info;
unsigned long fsr, tmpfsr, addr;
unsigned int gsr;
switch (sip->si_code) {
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:
if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
goto not_ieee;
break;
default:
goto not_ieee;
}
mode = FEX_NOHANDLER;
handler = oact.sa_handler;
thr_handlers = __fex_get_thr_handlers();
if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
mode = thr_handlers[(int)e].__mode;
handler = thr_handlers[(int)e].__handler;
}
i = ((int)uap->uc_mcontext.fpregs.fpu_fsr >> 5) & 0x1f;
__fex_mklog(uap, (char *)sip->si_addr, i, e, mode, (void *)handler);
if (mode == FEX_NOHANDLER)
goto not_ieee;
else if (mode == FEX_ABORT)
abort();
else if (mode == FEX_SIGNAL) {
handler(sig, sip, uap);
return;
}
__fenv_getfsr(&fsr);
__fenv_set_te(fsr, 0);
__fenv_set_ex(fsr, 0);
#ifdef __sparcv9
gsr = uap->uc_mcontext.asrs[3];
#else
gsr = 0;
if (uap->uc_mcontext.xrs.xrs_id == XRS_ID)
gsr = (*(unsigned long long*)((prxregset_t*)uap->uc_mcontext.
xrs.xrs_ptr)->pr_un.pr_v8p.pr_filler);
#endif
gsr = (gsr >> 25) & 7;
if (gsr & 4) {
siamp = (void (*)()) siam[0];
siamp();
tmpfsr = fsr;
fsr = (fsr & ~0xc0400000ul) | ((gsr & 3) << 30);
}
__fenv_setfsr(&fsr);
__fex_get_op(sip, uap, &info);
if (mode == FEX_CUSTOM) {
addr = (unsigned long)sip->si_addr;
if (addr >= (unsigned long)feraiseexcept &&
addr < (unsigned long)fetestexcept) {
info.op = fex_other;
info.op1.type = info.op2.type = info.res.type =
fex_nodata;
}
if (gsr & 4) {
__fenv_setfsr(&tmpfsr);
siamp = (void (*)()) siam[1 + (gsr & 3)];
siamp();
}
handler(1 << (int)e, &info);
if (gsr & 4) {
siamp = (void (*)()) siam[0];
siamp();
}
__fenv_setfsr(&fsr);
}
__fex_st_result(sip, uap, &info);
fsr = uap->uc_mcontext.fpregs.fpu_fsr;
fsr |= ((info.flags & 0x1f) << 5);
i = __fex_te_needed(thr_handlers, fsr);
__fenv_set_te(fsr, i);
uap->uc_mcontext.fpregs.fpu_fsr = fsr;
return;
not_ieee:
mutex_lock(&hdlr_lock);
act = oact;
mutex_unlock(&hdlr_lock);
switch ((unsigned long)act.sa_handler) {
case (unsigned long)SIG_DFL:
sigaction(SIGFPE, &act, NULL);
kill(getpid(), SIGFPE);
break;
#if !defined(__lint)
case (unsigned long)SIG_IGN:
break;
#endif
default:
act.sa_handler(sig, sip, uap);
}
}
#elif defined(__x86)
#if defined(__amd64)
#define test_sse_hw 1
#else
extern int _sse_hw;
#define test_sse_hw _sse_hw
#endif
#if !defined(REG_PC)
#define REG_PC EIP
#endif
static void
__fex_hdlr(int sig, siginfo_t *sip, void *arg)
{
ucontext_t *uap = arg;
struct fex_handler_data *thr_handlers;
struct sigaction act;
void (*handler)() = NULL, (*simd_handler[4])();
int mode, simd_mode[4], i, len, accrued, *ap;
unsigned int cwsw, oldcwsw, mxcsr, oldmxcsr;
enum fex_exception e, simd_e[4];
fex_info_t info, simd_info[4];
unsigned long addr;
siginfo_t osip = *sip;
sseinst_t inst;
if (!(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status & 0x80)) {
len = __fex_parse_sse(uap, &inst);
if (len == 0)
goto not_ieee;
__fenv_getcwsw(&oldcwsw);
cwsw = (oldcwsw & ~0x3f) | 0x003f0000;
__fenv_setcwsw(&cwsw);
__fenv_getmxcsr(&oldmxcsr);
mxcsr = (oldmxcsr & ~0x3f) | 0x1f80;
__fenv_setmxcsr(&mxcsr);
if ((int)inst.op & SIMD) {
__fex_get_simd_op(uap, &inst, simd_e, simd_info);
thr_handlers = __fex_get_thr_handlers();
addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
accrued = uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.mxcsr;
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;
simd_handler[i] = oact.sa_handler;
if (thr_handlers &&
thr_handlers[(int)e].__mode !=
FEX_NOHANDLER) {
simd_mode[i] =
thr_handlers[(int)e].__mode;
simd_handler[i] =
thr_handlers[(int)e].__handler;
}
accrued &= ~te_bit[(int)e];
switch (simd_mode[i]) {
case FEX_ABORT:
mode = FEX_ABORT;
break;
case FEX_SIGNAL:
if (mode != FEX_ABORT)
mode = FEX_SIGNAL;
handler = simd_handler[i];
break;
case FEX_NOHANDLER:
if (mode != FEX_ABORT && mode !=
FEX_SIGNAL)
mode = FEX_NOHANDLER;
break;
}
}
if (e == (enum fex_exception)-1) {
__fenv_setcwsw(&oldcwsw);
__fenv_setmxcsr(&oldmxcsr);
goto not_ieee;
}
accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.status;
ap = __fex_accrued();
accrued |= *ap;
accrued &= 0x3d;
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
__fex_mklog(uap, (char *)addr, accrued,
simd_e[i], simd_mode[i],
(void *)simd_handler[i]);
}
if (mode == FEX_NOHANDLER) {
__fenv_setcwsw(&oldcwsw);
__fenv_setmxcsr(&oldmxcsr);
goto not_ieee;
} else if (mode == FEX_ABORT) {
abort();
} else if (mode == FEX_SIGNAL) {
__fenv_setcwsw(&oldcwsw);
__fenv_setmxcsr(&oldmxcsr);
handler(sig, &osip, uap);
return;
}
*ap = 0;
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
if (simd_mode[i] == FEX_CUSTOM) {
handler(1 << (int)simd_e[i],
&simd_info[i]);
__fenv_setcwsw(&cwsw);
__fenv_setmxcsr(&mxcsr);
}
}
__fex_st_simd_result(uap, &inst, simd_e, simd_info);
for (i = 0; i < 4; i++) {
if ((int)simd_e[i] < 0)
continue;
accrued |= simd_info[i].flags;
}
if ((int)inst.op & INTREG) {
#if defined(__amd64)
uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.sw &= ~0x3800;
uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.fctw = 0;
#else
uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.state[1] &= ~0x3800;
uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.state[2] = 0;
#endif
}
} else {
e = __fex_get_sse_op(uap, &inst, &info);
if ((int)e < 0) {
__fenv_setcwsw(&oldcwsw);
__fenv_setmxcsr(&oldmxcsr);
goto not_ieee;
}
mode = FEX_NOHANDLER;
handler = oact.sa_handler;
thr_handlers = __fex_get_thr_handlers();
if (thr_handlers && thr_handlers[(int)e].__mode !=
FEX_NOHANDLER) {
mode = thr_handlers[(int)e].__mode;
handler = thr_handlers[(int)e].__handler;
}
addr = (unsigned long)uap->uc_mcontext.gregs[REG_PC];
accrued = uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.mxcsr & ~te_bit[(int)e];
accrued |= uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.status;
ap = __fex_accrued();
accrued |= *ap;
accrued &= 0x3d;
__fex_mklog(uap, (char *)addr, accrued, e, mode,
(void *)handler);
if (mode == FEX_NOHANDLER) {
__fenv_setcwsw(&oldcwsw);
__fenv_setmxcsr(&oldmxcsr);
goto not_ieee;
} else if (mode == FEX_ABORT) {
abort();
} else if (mode == FEX_SIGNAL) {
__fenv_setcwsw(&oldcwsw);
__fenv_setmxcsr(&oldmxcsr);
handler(sig, &osip, uap);
return;
} else if (mode == FEX_CUSTOM) {
*ap = 0;
if (addr >= (unsigned long)feraiseexcept &&
addr < (unsigned long)fetestexcept) {
info.op = fex_other;
info.op1.type = info.op2.type =
info.res.type = fex_nodata;
}
handler(1 << (int)e, &info);
__fenv_setcwsw(&cwsw);
__fenv_setmxcsr(&mxcsr);
}
__fex_st_sse_result(uap, &inst, e, &info);
accrued |= info.flags;
#if defined(__amd64)
if (inst.op == cvtss2si || inst.op == cvttss2si ||
inst.op == cvtsd2si || inst.op == cvttsd2si)
inst.op1->i[1] = 0;
#endif
}
uap->uc_mcontext.gregs[REG_PC] += len;
goto update_state;
}
__fex_get_x86_exc(sip, uap);
switch (sip->si_code) {
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:
if ((int)(e = __fex_get_invalid_type(sip, uap)) < 0)
goto not_ieee;
break;
default:
goto not_ieee;
}
mode = FEX_NOHANDLER;
handler = oact.sa_handler;
thr_handlers = __fex_get_thr_handlers();
if (thr_handlers && thr_handlers[(int)e].__mode != FEX_NOHANDLER) {
mode = thr_handlers[(int)e].__mode;
handler = thr_handlers[(int)e].__handler;
}
#if defined(__amd64)
addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.rip;
#else
addr = (unsigned long)uap->uc_mcontext.fpregs.fp_reg_set.
fpchip_state.state[3];
#endif
accrued = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status &
~te_bit[(int)e];
if (test_sse_hw)
accrued |= uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.
mxcsr;
ap = __fex_accrued();
accrued |= *ap;
accrued &= 0x3d;
__fex_mklog(uap, (char *)addr, accrued, e, mode, (void *)handler);
if (mode == FEX_NOHANDLER)
goto not_ieee;
else if (mode == FEX_ABORT)
abort();
else if (mode == FEX_SIGNAL) {
handler(sig, &osip, uap);
return;
}
__fenv_getcwsw(&cwsw);
cwsw = (cwsw & ~0x3f) | 0x003f0000;
__fenv_setcwsw(&cwsw);
if (test_sse_hw) {
__fenv_getmxcsr(&mxcsr);
mxcsr = (mxcsr & ~0x3f) | 0x1f80;
__fenv_setmxcsr(&mxcsr);
}
*ap = 0;
__fex_get_op(sip, uap, &info);
if (mode == FEX_CUSTOM) {
if (addr >= (unsigned long)feraiseexcept &&
addr < (unsigned long)fetestexcept) {
info.op = fex_other;
info.op1.type = info.op2.type = info.res.type =
fex_nodata;
}
handler(1 << (int)e, &info);
__fenv_setcwsw(&cwsw);
if (test_sse_hw)
__fenv_setmxcsr(&mxcsr);
}
__fex_st_result(sip, uap, &info);
accrued |= info.flags;
update_state:
accrued &= 0x3d;
i = __fex_te_needed(thr_handlers, accrued);
*ap = accrued & i;
#if defined(__amd64)
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw &= ~0x3d;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= (accrued & ~i);
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw |= 0x3d;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw &= ~i;
#else
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] &= ~0x3d;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1] |=
(accrued & ~i);
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] |= 0x3d;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0] &= ~i;
#endif
if (test_sse_hw) {
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &= ~0x3d;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr |=
0x1e80 | (accrued & ~i);
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr &=
~(i << 7);
}
return;
not_ieee:
mutex_lock(&hdlr_lock);
act = oact;
mutex_unlock(&hdlr_lock);
switch ((unsigned long)act.sa_handler) {
case (unsigned long)SIG_DFL:
sigaction(SIGFPE, &act, NULL);
kill(getpid(), SIGFPE);
break;
#if !defined(__lint)
case (unsigned long)SIG_IGN:
break;
#endif
default:
act.sa_sigaction(sig, &osip, uap);
}
}
#else
#error Unknown architecture
#endif
struct fex_handler_data *
__fex_get_thr_handlers()
{
struct fex_handler_data *ptr;
unsigned long fsr;
int i, te;
if (thr_main()) {
if (!handlers_initialized) {
__fenv_getfsr(&fsr);
te = (int)__fenv_get_te(fsr);
for (i = 0; i < FEX_NUM_EXC; i++)
main_handlers[i].__mode =
((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
handlers_initialized = 1;
}
return main_handlers;
}
else {
ptr = NULL;
mutex_lock(&handlers_key_lock);
if (thr_getspecific(handlers_key, (void **)&ptr) != 0 &&
thr_keycreate(&handlers_key, free) != 0) {
mutex_unlock(&handlers_key_lock);
return NULL;
}
mutex_unlock(&handlers_key_lock);
if (!ptr) {
if ((ptr = (struct fex_handler_data *)
malloc(sizeof(fex_handler_t))) == NULL) {
return NULL;
}
if (thr_setspecific(handlers_key, (void *)ptr) != 0) {
(void)free(ptr);
return NULL;
}
__fenv_getfsr(&fsr);
te = (int)__fenv_get_te(fsr);
for (i = 0; i < FEX_NUM_EXC; i++)
ptr[i].__mode = ((te & te_bit[i])? FEX_NOHANDLER : FEX_NONSTOP);
}
return ptr;
}
}
void
__fex_update_te()
{
struct fex_handler_data *thr_handlers;
struct sigaction act, tmpact;
sigset_t blocked;
unsigned long fsr;
int te;
thr_handlers = __fex_get_thr_handlers();
__fenv_getfsr(&fsr);
te = __fex_te_needed(thr_handlers, fsr);
if (!hdlr_installed && te) {
act.sa_sigaction = __fex_hdlr;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGFPE, &act, &tmpact);
if (tmpact.sa_sigaction != __fex_hdlr)
{
mutex_lock(&hdlr_lock);
oact = tmpact;
mutex_unlock(&hdlr_lock);
}
hdlr_installed = 1;
}
if (sigprocmask(0, NULL, &blocked) == 0 &&
!sigismember(&blocked, SIGFPE)) {
__fenv_set_te(fsr, te);
__fenv_setfsr(&fsr);
}
__mt_fex_sync = __fex_sync_with_libmtsk;
__libm_mt_fex_sync = __fex_sync_with_threads;
}