#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <siginfo.h>
#include <ucontext.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(__amd64)
#define test_sse_hw 1
#else
extern int _sse_hw;
#define test_sse_hw _sse_hw
#endif
static int accrued = 0;
static thread_key_t accrued_key;
static mutex_t accrued_key_lock = DEFAULTMUTEX;
int *
__fex_accrued()
{
int *p;
if (thr_main())
return &accrued;
else {
p = NULL;
mutex_lock(&accrued_key_lock);
if (thr_getspecific(accrued_key, (void **)&p) != 0 &&
thr_keycreate(&accrued_key, free) != 0) {
mutex_unlock(&accrued_key_lock);
return NULL;
}
mutex_unlock(&accrued_key_lock);
if (!p) {
if ((p = (int*) malloc(sizeof(int))) == NULL)
return NULL;
if (thr_setspecific(accrued_key, (void *)p) != 0) {
(void)free(p);
return NULL;
}
*p = 0;
}
return p;
}
}
void
__fenv_getfsr(unsigned long *fsr)
{
unsigned int cwsw, mxcsr;
__fenv_getcwsw(&cwsw);
cwsw &= ~0xe0c00000u;
if (test_sse_hw) {
__fenv_getmxcsr(&mxcsr);
cwsw |= (mxcsr & 0x3d);
}
cwsw |= *__fex_accrued();
*fsr = cwsw ^ 0x003f0000u;
}
void
__fenv_setfsr(const unsigned long *fsr)
{
unsigned int cwsw, mxcsr;
int te;
cwsw = (unsigned int)*fsr;
te = __fenv_get_te(cwsw);
*__fex_accrued() = cwsw & te;
cwsw = (cwsw & ~te) ^ 0x003f0000;
if (test_sse_hw) {
__fenv_getmxcsr(&mxcsr);
mxcsr = (mxcsr & ~0x7ebd) | ((cwsw >> 13) & 0x6000) |
((cwsw >> 9) & 0x1e80) | (cwsw & 0x3d);
__fenv_setmxcsr(&mxcsr);
}
__fenv_setcwsw(&cwsw);
}
#define CW 0
#define SW 1
#define TW 2
#define IP 3
#define OP 4
#define EA 5
#if defined(__amd64)
#define fpreg(u,x) *(long double *)(10*(x)+(char*)&(u)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.st)
#else
#define fpreg(u,x) *(long double *)(10*(x)+(char*)&(u)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[7])
#endif
void
__fex_get_x86_exc(siginfo_t *sip, ucontext_t *uap)
{
unsigned sw, cw;
sw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status;
#if defined(__amd64)
cw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw;
#else
cw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[CW];
#endif
if ((sw & FE_INVALID) && !(cw & (1 << fp_trap_invalid)))
sip->si_code = ((sw & 0x40)? 0 : FPE_FLTINV);
else if ((sw & FE_DIVBYZERO) && !(cw & (1 << fp_trap_division)))
sip->si_code = FPE_FLTDIV;
else if ((sw & FE_OVERFLOW) && !(cw & (1 << fp_trap_overflow)))
sip->si_code = FPE_FLTOVF;
else if ((sw & FE_UNDERFLOW) && !(cw & (1 << fp_trap_underflow)))
sip->si_code = FPE_FLTUND;
else if ((sw & FE_INEXACT) && !(cw & (1 << fp_trap_inexact)))
sip->si_code = FPE_FLTRES;
else
sip->si_code = 0;
}
static enum fp_class_type
my_fp_classf(float *x)
{
int i = *(int*)x & ~0x80000000;
if (i < 0x7f800000) {
if (i < 0x00800000)
return ((i == 0)? fp_zero : fp_subnormal);
return fp_normal;
}
else if (i == 0x7f800000)
return fp_infinity;
else if (i & 0x400000)
return fp_quiet;
else
return fp_signaling;
}
static enum fp_class_type
my_fp_class(double *x)
{
int i = *(1+(int*)x) & ~0x80000000;
if (i < 0x7ff00000) {
if (i < 0x00100000)
return (((i | *(int*)x) == 0)? fp_zero : fp_subnormal);
return fp_normal;
}
else if (i == 0x7ff00000 && *(int*)x == 0)
return fp_infinity;
else if (i & 0x80000)
return fp_quiet;
else
return fp_signaling;
}
static enum fp_class_type
my_fp_classl(long double *x)
{
int i = *(2+(int*)x) & 0x7fff;
if (i < 0x7fff) {
if (i < 1) {
if (*(1+(int*)x) < 0) return fp_normal;
return (((*(1+(int*)x) | *(int*)x) == 0)?
fp_zero : fp_subnormal);
}
return ((*(1+(int*)x) < 0)? fp_normal :
(enum fp_class_type) -1);
}
else if (*(1+(int*)x) == 0x80000000 && *(int*)x == 0)
return fp_infinity;
else if (*(1+(unsigned*)x) >= 0xc0000000)
return fp_quiet;
else if (*(1+(int*)x) < 0)
return fp_signaling;
else
return (enum fp_class_type) -1;
}
enum fex_exception
__fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
{
unsigned op;
unsigned long ea;
enum fp_class_type t1, t2;
#if defined(__amd64)
op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
#else
op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
#endif
switch (op & 0x7f8) {
case 0x100:
case 0x140:
case 0x180:
case 0x500:
case 0x540:
case 0x580:
return fex_inv_snan;
}
t1 = my_fp_classl(&fpreg(uap, 0));
if (t1 == fp_signaling)
return fex_inv_snan;
else if (t1 == (enum fp_class_type) -1)
return (enum fex_exception) -1;
t2 = fp_normal;
switch (op & 0x7e0) {
case 0x600:
case 0x620:
case 0x640:
case 0x660:
case 0x680:
case 0x6a0:
if (!ea)
return (enum fex_exception) -1;
if (*(short *)ea == 0)
t2 = fp_zero;
break;
case 0x200:
case 0x220:
case 0x240:
case 0x260:
case 0x280:
case 0x2a0:
if (!ea)
return (enum fex_exception) -1;
if (*(int *)ea == 0)
t2 = fp_zero;
break;
case 0x000:
case 0x020:
case 0x040:
case 0x060:
case 0x080:
case 0x0a0:
if (!ea)
return (enum fex_exception) -1;
t2 = my_fp_classf((float *)ea);
break;
case 0x400:
case 0x420:
case 0x440:
case 0x460:
case 0x480:
case 0x4a0:
if (!ea)
return (enum fex_exception) -1;
t2 = my_fp_class((double *)ea);
break;
case 0x0c0:
case 0x0e0:
case 0x3e0:
case 0x4c0:
case 0x4e0:
case 0x5e0:
case 0x6c0:
case 0x6e0:
case 0x7e0:
switch (op & 0x7f8) {
case 0x3e0:
case 0x3f8:
case 0x5f0:
case 0x5f8:
case 0x7e0:
case 0x7f8:
break;
default:
t2 = my_fp_classl(&fpreg(uap, op & 7));
}
break;
case 0x1e0:
case 0x2e0:
switch (op) {
case 0x1f1:
case 0x1f3:
case 0x1f5:
case 0x1f8:
case 0x1f9:
case 0x1fd:
case 0x2e9:
t2 = my_fp_classl(&fpreg(uap, 1));
break;
}
break;
}
if (t2 == fp_signaling)
return fex_inv_snan;
else if (t2 == (enum fp_class_type) -1)
return (enum fex_exception) -1;
switch (op & 0x7f8) {
case 0x000:
case 0x020:
case 0x028:
case 0x040:
case 0x060:
case 0x068:
case 0x080:
case 0x0a0:
case 0x0a8:
case 0x0c0:
case 0x0e0:
case 0x0e8:
case 0x400:
case 0x420:
case 0x428:
case 0x440:
case 0x460:
case 0x468:
case 0x480:
case 0x4a0:
case 0x4a8:
case 0x4c0:
case 0x4e0:
case 0x4e8:
case 0x6c0:
case 0x6e0:
case 0x6e8:
if (t1 == fp_infinity && t2 == fp_infinity)
return fex_inv_isi;
break;
case 0x008:
case 0x048:
case 0x088:
case 0x0c8:
case 0x208:
case 0x248:
case 0x288:
case 0x408:
case 0x448:
case 0x488:
case 0x4c8:
case 0x608:
case 0x648:
case 0x688:
case 0x6c8:
if ((t1 == fp_zero && t2 == fp_infinity) || (t2 == fp_zero &&
t1 == fp_infinity))
return fex_inv_zmi;
break;
case 0x030:
case 0x038:
case 0x070:
case 0x078:
case 0x0b0:
case 0x0b8:
case 0x0f0:
case 0x0f8:
case 0x230:
case 0x238:
case 0x270:
case 0x278:
case 0x2b0:
case 0x2b8:
case 0x430:
case 0x438:
case 0x470:
case 0x478:
case 0x4b0:
case 0x4b8:
case 0x4f0:
case 0x4f8:
case 0x630:
case 0x638:
case 0x670:
case 0x678:
case 0x6b0:
case 0x6b8:
case 0x6f0:
case 0x6f8:
if (t1 == fp_zero && t2 == fp_zero)
return fex_inv_zdz;
else if (t1 == fp_infinity && t2 == fp_infinity)
return fex_inv_idi;
break;
case 0x1f0:
case 0x1f8:
return fex_inv_sqrt;
case 0x010:
case 0x018:
case 0x050:
case 0x058:
case 0x090:
case 0x098:
case 0x0d0:
case 0x0d8:
case 0x210:
case 0x218:
case 0x250:
case 0x258:
case 0x290:
case 0x298:
case 0x2e8:
case 0x3f0:
case 0x410:
case 0x418:
case 0x450:
case 0x458:
case 0x490:
case 0x498:
case 0x4d0:
case 0x4d8:
case 0x5e0:
case 0x5e8:
case 0x610:
case 0x618:
case 0x650:
case 0x658:
case 0x690:
case 0x698:
case 0x6d0:
case 0x6d8:
case 0x7f0:
if (t1 == fp_quiet || t2 == fp_quiet)
return fex_inv_cmp;
break;
case 0x1e0:
if (op == 0x1e4 && t1 == fp_quiet)
return fex_inv_cmp;
break;
case 0x310:
case 0x318:
case 0x350:
case 0x358:
case 0x390:
case 0x398:
case 0x710:
case 0x718:
case 0x730:
case 0x738:
case 0x750:
case 0x758:
case 0x770:
case 0x778:
case 0x790:
case 0x798:
case 0x7b0:
case 0x7b8:
return fex_inv_int;
}
return (enum fex_exception) -1;
}
static const long double
two12288 = 1.139165225263043370845938579315932009e+3699l,
twom12288 = 8.778357852076208839765066529179033145e-3700l,
twom12288mulp = 8.778357852076208839289190796475222545e-3700l;
extern long double f2xm1(long double);
extern long double fyl2x(long double, long double);
extern long double fptan(long double);
extern long double fpatan(long double, long double);
extern long double fxtract(long double);
extern long double fprem1(long double, long double);
extern long double fprem(long double, long double);
extern long double fyl2xp1(long double, long double);
extern long double fsqrt(long double);
extern long double fsincos(long double);
extern long double frndint(long double);
extern long double fscale(long double, long double);
extern long double fsin(long double);
extern long double fcos(long double);
void
__fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
{
fex_numeric_t t;
long double op2v, x;
unsigned int cwsw, ex, sw, op;
unsigned long ea;
volatile int c __unused;
ex = sip->si_code;
sw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status;
#if defined(__amd64)
op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
#else
op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
#endif
info->op = fex_cnvt;
info->op2.type = fex_nodata;
switch (op & 0x7f8) {
case 0x100:
case 0x140:
case 0x180:
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_float;
info->op1.val.f = *(float *)ea;
info->res.type = fex_ldouble;
info->res.val.q = (long double) info->op1.val.f;
goto done;
case 0x500:
case 0x540:
case 0x580:
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_double;
info->op1.val.d = *(double *)ea;
info->res.type = fex_ldouble;
info->res.val.q = (long double) info->op1.val.d;
goto done;
case 0x110:
case 0x118:
case 0x150:
case 0x158:
case 0x190:
case 0x198:
info->res.type = fex_float;
if (ex == FPE_FLTRES && (op & 8) != 0) {
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_nodata;
info->res.val.f = *(float *)ea;
info->flags = FE_INEXACT;
return;
}
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
info->res.val.f = (float) info->op1.val.q;
goto done;
case 0x310:
case 0x318:
case 0x350:
case 0x358:
case 0x390:
case 0x398:
info->res.type = fex_int;
if (ex == FPE_FLTRES && (op & 8) != 0) {
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_nodata;
info->res.val.i = *(int *)ea;
info->flags = FE_INEXACT;
return;
}
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
info->res.val.i = (int) info->op1.val.q;
goto done;
case 0x510:
case 0x518:
case 0x550:
case 0x558:
case 0x590:
case 0x598:
info->res.type = fex_double;
if (ex == FPE_FLTRES && (op & 8) != 0) {
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_nodata;
info->res.val.d = *(double *)ea;
info->flags = FE_INEXACT;
return;
}
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
info->res.val.d = (double) info->op1.val.q;
goto done;
case 0x710:
case 0x718:
case 0x750:
case 0x758:
case 0x790:
case 0x798:
info->res.type = fex_int;
if (ex == FPE_FLTRES && (op & 8) != 0) {
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_nodata;
info->res.val.i = *(short *)ea;
info->flags = FE_INEXACT;
return;
}
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
info->res.val.i = (short) info->op1.val.q;
goto done;
case 0x730:
case 0x770:
case 0x7b0:
info->op = fex_other;
info->op1.type = info->res.type = fex_nodata;
info->flags = 0;
return;
case 0x738:
case 0x778:
case 0x7b8:
info->res.type = fex_llong;
if (ex == FPE_FLTRES) {
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op1.type = fex_nodata;
info->res.val.l = *(long long *)ea;
info->flags = FE_INEXACT;
return;
}
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
info->res.val.l = (long long) info->op1.val.q;
goto done;
}
if (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES) {
info->op1.type = info->op2.type = fex_nodata;
info->res.type = fex_ldouble;
switch (op & 0x7f8) {
case 0x1f0:
info->res.val.q = ((op == 0x1f2)? fpreg(uap, 1) :
fpreg(uap, 0));
break;
case 0x4c0:
case 0x4c8:
case 0x4e0:
case 0x4e8:
case 0x4f0:
case 0x4f8:
info->res.val.q = fpreg(uap, op & 7);
break;
case 0x6c0:
case 0x6c8:
case 0x6e0:
case 0x6e8:
case 0x6f0:
case 0x6f8:
info->res.val.q = fpreg(uap, (op - 1) & 7);
break;
default:
info->res.val.q = fpreg(uap, 0);
}
if (ex == FPE_FLTOVF) {
x = two12288;
*(4+(short*)&x) |= (*(4+(short*)&info->res.val.q) & 0x8000);
info->res.val.q = x * two12288;
info->flags = FE_OVERFLOW | FE_INEXACT;
__fenv_getcwsw(&cwsw);
cwsw &= ~FE_ALL_EXCEPT;
__fenv_setcwsw(&cwsw);
}
else if (ex == FPE_FLTUND) {
if (sw & 0x200)
info->res.val.q = (info->res.val.q * twom12288) * twom12288mulp;
else
info->res.val.q = (info->res.val.q * twom12288) * twom12288;
__fenv_getcwsw(&cwsw);
info->flags = (cwsw & FE_INEXACT) | FE_UNDERFLOW;
cwsw &= ~FE_ALL_EXCEPT;
__fenv_setcwsw(&cwsw);
}
else
info->flags = FE_INEXACT;
switch (op) {
case 0x1f0:
case 0x1f1:
case 0x1f2:
case 0x1f3:
case 0x1f5:
case 0x1f8:
case 0x1f9:
case 0x1fb:
case 0x1fc:
case 0x1fd:
case 0x1fe:
case 0x1ff:
info->op = fex_other;
return;
case 0x1fa:
info->op = fex_sqrt;
return;
}
info->op = fex_other;
switch (op & 0x7c0) {
case 0x000:
case 0x040:
case 0x080:
case 0x0c0:
case 0x200:
case 0x240:
case 0x280:
case 0x400:
case 0x440:
case 0x480:
case 0x4c0:
case 0x600:
case 0x640:
case 0x680:
case 0x6c0:
switch (op & 0x38) {
case 0x00:
info->op = fex_add;
break;
case 0x08:
info->op = fex_mul;
break;
case 0x20:
case 0x28:
info->op = fex_sub;
break;
case 0x30:
case 0x38:
info->op = fex_div;
break;
}
}
return;
}
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
info->op = fex_other;
switch (op) {
case 0x1e4:
info->op = fex_cmp;
info->op2.type = fex_ldouble;
info->op2.val.q = 0.0l;
info->res.type = fex_nodata;
c = (info->op1.val.q < info->op2.val.q);
goto done;
case 0x1f0:
info->res.type = fex_ldouble;
info->res.val.q = f2xm1(info->op1.val.q);
goto done;
case 0x1f1:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_ldouble;
info->res.val.q = fyl2x(info->op1.val.q, info->op2.val.q);
goto done;
case 0x1f2:
info->res.type = fex_ldouble;
info->res.val.q = fptan(info->op1.val.q);
goto done;
case 0x1f3:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_ldouble;
info->res.val.q = fpatan(info->op1.val.q, info->op2.val.q);
goto done;
case 0x1f4:
info->res.type = fex_ldouble;
info->res.val.q = fxtract(info->op1.val.q);
goto done;
case 0x1f5:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_ldouble;
info->res.val.q = fprem1(info->op1.val.q, info->op2.val.q);
goto done;
case 0x1f8:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_ldouble;
info->res.val.q = fprem(info->op1.val.q, info->op2.val.q);
goto done;
case 0x1f9:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_ldouble;
info->res.val.q = fyl2xp1(info->op1.val.q, info->op2.val.q);
goto done;
case 0x1fa:
info->op = fex_sqrt;
info->res.type = fex_ldouble;
info->res.val.q = fsqrt(info->op1.val.q);
goto done;
case 0x1fb:
info->res.type = fex_ldouble;
info->res.val.q = fsincos(info->op1.val.q);
goto done;
case 0x1fc:
info->res.type = fex_ldouble;
info->res.val.q = frndint(info->op1.val.q);
goto done;
case 0x1fd:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_ldouble;
info->res.val.q = fscale(info->op1.val.q, info->op2.val.q);
goto done;
case 0x1fe:
info->res.type = fex_ldouble;
info->res.val.q = fsin(info->op1.val.q);
goto done;
case 0x1ff:
info->res.type = fex_ldouble;
info->res.val.q = fcos(info->op1.val.q);
goto done;
case 0x2e9:
info->op = fex_cmp;
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, 1);
info->res.type = fex_nodata;
c = (info->op1.val.q == info->op2.val.q);
goto done;
}
switch (op & 0x7f8) {
case 0x3e8:
case 0x5e0:
case 0x5e8:
case 0x7e8:
info->op = fex_cmp;
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, op & 7);
info->res.type = fex_nodata;
c = (info->op1.val.q == info->op2.val.q);
goto done;
case 0x3f0:
case 0x7f0:
info->op = fex_cmp;
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, op & 7);
info->res.type = fex_nodata;
c = (info->op1.val.q < info->op2.val.q);
goto done;
}
switch (op & 0x7c0) {
case 0x000:
case 0x040:
case 0x080:
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op2.type = fex_float;
info->op2.val.f = *(float *)ea;
op2v = (long double) info->op2.val.f;
break;
case 0x0c0:
info->op2.type = fex_ldouble;
op2v = info->op2.val.q = fpreg(uap, op & 7);
break;
case 0x200:
case 0x240:
case 0x280:
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op2.type = fex_int;
info->op2.val.i = *(int *)ea;
op2v = (long double) info->op2.val.i;
break;
case 0x400:
case 0x440:
case 0x480:
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op2.type = fex_double;
info->op2.val.d = *(double *)ea;
op2v = (long double) info->op2.val.d;
break;
case 0x4c0:
case 0x6c0:
info->op2.type = fex_ldouble;
info->op2.val.q = fpreg(uap, op & 7);
t = info->op1;
info->op1 = info->op2;
info->op2 = t;
op2v = info->op2.val.q;
break;
case 0x600:
case 0x640:
case 0x680:
if (!ea) {
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->op2.type = fex_int;
info->op2.val.i = *(short *)ea;
op2v = (long double) info->op2.val.i;
break;
default:
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
info->res.type = fex_ldouble;
switch (op & 0x38) {
case 0x00:
info->op = fex_add;
info->res.val.q = info->op1.val.q + op2v;
break;
case 0x08:
info->op = fex_mul;
info->res.val.q = info->op1.val.q * op2v;
break;
case 0x10:
case 0x18:
info->op = fex_cmp;
info->res.type = fex_nodata;
c = (info->op1.val.q < op2v);
break;
case 0x20:
info->op = fex_sub;
info->res.val.q = info->op1.val.q - op2v;
break;
case 0x28:
info->op = fex_sub;
info->res.val.q = op2v - info->op1.val.q;
t = info->op1;
info->op1 = info->op2;
info->op2 = t;
break;
case 0x30:
info->op = fex_div;
info->res.val.q = info->op1.val.q / op2v;
break;
case 0x38:
info->op = fex_div;
info->res.val.q = op2v / info->op1.val.q;
t = info->op1;
info->op1 = info->op2;
info->op2 = t;
break;
default:
info->op = fex_other;
info->op1.type = info->op2.type = info->res.type = fex_nodata;
info->flags = 0;
return;
}
done:
__fenv_getcwsw(&cwsw);
info->flags = cwsw & FE_ALL_EXCEPT;
cwsw &= ~FE_ALL_EXCEPT;
__fenv_setcwsw(&cwsw);
}
static void pop(ucontext_t *uap)
{
unsigned top;
fpreg(uap, 0) = fpreg(uap, 1);
fpreg(uap, 1) = fpreg(uap, 2);
fpreg(uap, 2) = fpreg(uap, 3);
fpreg(uap, 3) = fpreg(uap, 4);
fpreg(uap, 4) = fpreg(uap, 5);
fpreg(uap, 5) = fpreg(uap, 6);
fpreg(uap, 6) = fpreg(uap, 7);
#if defined(__amd64)
top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw >> 10)
& 0xe;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fctw |= (3 << top);
top = (top + 2) & 0xe;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw =
(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw & ~0x3800)
| (top << 10);
#else
top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] >> 10)
& 0xe;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[TW] |= (3 << top);
top = (top + 2) & 0xe;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] =
(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] & ~0x3800)
| (top << 10);
#endif
}
static void push(long double x, ucontext_t *uap)
{
unsigned top;
fpreg(uap, 7) = fpreg(uap, 6);
fpreg(uap, 6) = fpreg(uap, 5);
fpreg(uap, 5) = fpreg(uap, 4);
fpreg(uap, 4) = fpreg(uap, 3);
fpreg(uap, 3) = fpreg(uap, 2);
fpreg(uap, 2) = fpreg(uap, 1);
fpreg(uap, 1) = fpreg(uap, 0);
fpreg(uap, 0) = x;
#if defined(__amd64)
top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw >> 10)
& 0xe;
top = (top - 2) & 0xe;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fctw &= ~(3 << top);
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw =
(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw & ~0x3800)
| (top << 10);
#else
top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] >> 10)
& 0xe;
top = (top - 2) & 0xe;
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[TW] &= ~(3 << top);
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] =
(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] & ~0x3800)
| (top << 10);
#endif
}
static const float
fun = 7.922816251e+28f,
fov = 1.262177448e-29f;
static const double
dun = 1.552518092300708935e+231,
dov = 6.441148769597133308e-232;
void
__fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
{
fex_numeric_t r;
unsigned long ex, op, ea, stack;
ex = sip->si_code;
#if defined(__amd64)
op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
#else
op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
#endif
switch (op & 0x7f8) {
case 0x010:
case 0x050:
case 0x090:
case 0x0d0:
case 0x210:
case 0x250:
case 0x290:
case 0x410:
case 0x450:
case 0x490:
case 0x4d0:
case 0x5e0:
case 0x610:
case 0x650:
case 0x690:
#if defined(__amd64)
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
#else
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
#endif
return;
case 0x018:
case 0x058:
case 0x098:
case 0x0d8:
case 0x218:
case 0x258:
case 0x298:
case 0x418:
case 0x458:
case 0x498:
case 0x4d8:
case 0x5e8:
case 0x618:
case 0x658:
case 0x698:
case 0x6d0:
#if defined(__amd64)
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
#else
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
#endif
pop(uap);
return;
case 0x2e8:
case 0x6d8:
#if defined(__amd64)
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
#else
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
#endif
pop(uap);
pop(uap);
return;
case 0x1e0:
if (op == 0x1e4) {
#if defined(__amd64)
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
#else
uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
#endif
return;
}
break;
case 0x3e8:
case 0x3f0:
#if defined(__amd64)
uap->uc_mcontext.gregs[REG_PS] |= 0x45;
#else
uap->uc_mcontext.gregs[EFL] |= 0x45;
#endif
return;
case 0x7e8:
case 0x7f0:
#if defined(__amd64)
uap->uc_mcontext.gregs[REG_PS] |= 0x45;
#else
uap->uc_mcontext.gregs[EFL] |= 0x45;
#endif
pop(uap);
return;
}
r = info->res;
if (r.type == fex_nodata) {
if (ex == FPE_FLTOVF || ex == FPE_FLTUND) {
switch (op & 0x7f8) {
case 0x110:
case 0x118:
case 0x150:
case 0x158:
case 0x190:
case 0x198:
if (!ea)
return;
if (ex == FPE_FLTOVF)
*(float *)ea = (fpreg(uap, 0) * fov) * fov;
else
*(float *)ea = (fpreg(uap, 0) * fun) * fun;
if ((op & 8) != 0)
pop(uap);
break;
case 0x510:
case 0x518:
case 0x550:
case 0x558:
case 0x590:
case 0x598:
if (!ea)
return;
if (ex == FPE_FLTOVF)
*(double *)ea = (fpreg(uap, 0) * dov) * dov;
else
*(double *)ea = (fpreg(uap, 0) * dun) * dun;
if ((op & 8) != 0)
pop(uap);
break;
}
}
#ifdef DEBUG
else if (ex != FPE_FLTRES)
printf("No result supplied, stack may be hosed\n");
#endif
return;
}
switch (op & 0x7f8) {
case 0x110:
case 0x118:
case 0x150:
case 0x158:
case 0x190:
case 0x198:
if (!ea)
return;
switch (r.type) {
case fex_int:
*(float *)ea = (float) r.val.i;
break;
case fex_llong:
*(float *)ea = (float) r.val.l;
break;
case fex_float:
*(float *)ea = r.val.f;
break;
case fex_double:
*(float *)ea = (float) r.val.d;
break;
case fex_ldouble:
*(float *)ea = (float) r.val.q;
break;
default:
break;
}
if (ex != FPE_FLTRES && (op & 8) != 0)
pop(uap);
return;
case 0x310:
case 0x318:
case 0x350:
case 0x358:
case 0x390:
case 0x398:
if (!ea)
return;
switch (r.type) {
case fex_int:
*(int *)ea = r.val.i;
break;
case fex_llong:
*(int *)ea = (int) r.val.l;
break;
case fex_float:
*(int *)ea = (int) r.val.f;
break;
case fex_double:
*(int *)ea = (int) r.val.d;
break;
case fex_ldouble:
*(int *)ea = (int) r.val.q;
break;
default:
break;
}
if (ex != FPE_FLTRES && (op & 8) != 0)
pop(uap);
return;
case 0x510:
case 0x518:
case 0x550:
case 0x558:
case 0x590:
case 0x598:
if (!ea)
return;
switch (r.type) {
case fex_int:
*(double *)ea = (double) r.val.i;
break;
case fex_llong:
*(double *)ea = (double) r.val.l;
break;
case fex_float:
*(double *)ea = (double) r.val.f;
break;
case fex_double:
*(double *)ea = r.val.d;
break;
case fex_ldouble:
*(double *)ea = (double) r.val.q;
break;
default:
break;
}
if (ex != FPE_FLTRES && (op & 8) != 0)
pop(uap);
return;
case 0x710:
case 0x718:
case 0x750:
case 0x758:
case 0x790:
case 0x798:
if (!ea)
return;
switch (r.type) {
case fex_int:
*(short *)ea = (short) r.val.i;
break;
case fex_llong:
*(short *)ea = (short) r.val.l;
break;
case fex_float:
*(short *)ea = (short) r.val.f;
break;
case fex_double:
*(short *)ea = (short) r.val.d;
break;
case fex_ldouble:
*(short *)ea = (short) r.val.q;
break;
default:
break;
}
if (ex != FPE_FLTRES && (op & 8) != 0)
pop(uap);
return;
case 0x730:
case 0x770:
case 0x7b0:
if (ea && ex != FPE_FLTRES)
pop(uap);
return;
case 0x738:
case 0x778:
case 0x7b8:
if (!ea)
return;
switch (r.type) {
case fex_int:
*(long long *)ea = (long long) r.val.i;
break;
case fex_llong:
*(long long *)ea = r.val.l;
break;
case fex_float:
*(long long *)ea = (long long) r.val.f;
break;
case fex_double:
*(long long *)ea = (long long) r.val.d;
break;
case fex_ldouble:
*(long long *)ea = (long long) r.val.q;
break;
default:
break;
}
if (ex != FPE_FLTRES)
pop(uap);
return;
}
switch (r.type) {
case fex_int:
r.val.q = (long double) r.val.i;
break;
case fex_llong:
r.val.q = (long double) r.val.l;
break;
case fex_float:
r.val.q = (long double) r.val.f;
break;
case fex_double:
r.val.q = (long double) r.val.d;
break;
default:
break;
}
switch (op & 0x7f8) {
case 0x100:
case 0x140:
case 0x180:
case 0x500:
case 0x540:
case 0x580:
if (ea)
push(r.val.q, uap);
return;
}
stack = (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES);
switch (op & 0x7f8) {
case 0x1f0:
switch (op) {
case 0x1f1:
case 0x1f3:
case 0x1f9:
if (!stack)
pop(uap);
fpreg(uap, 0) = r.val.q;
return;
case 0x1f2:
if (stack)
fpreg(uap, 1) = r.val.q;
else {
fpreg(uap, 0) = r.val.q;
push(1.0L, uap);
}
return;
case 0x1f4:
case 0x1fb:
if (stack)
fpreg(uap, 0) = r.val.q;
else {
fpreg(uap, 0) = 0.0;
push(r.val.q, uap);
}
return;
}
fpreg(uap, 0) = r.val.q;
return;
case 0x4c0:
case 0x4c8:
case 0x4e0:
case 0x4e8:
case 0x4f0:
case 0x4f8:
fpreg(uap, op & 7) = r.val.q;
return;
case 0x6c0:
case 0x6c8:
case 0x6e0:
case 0x6e8:
case 0x6f0:
case 0x6f8:
if (stack)
fpreg(uap, (op - 1) & 7) = r.val.q;
else {
fpreg(uap, op & 7) = r.val.q;
pop(uap);
}
return;
default:
fpreg(uap, 0) = r.val.q;
return;
}
}