#if defined(__sparc)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <siginfo.h>
#include <thread.h>
#include <ucontext.h>
#include <math.h>
#if defined(__SUNPRO_C)
#include <sunmath.h>
#endif
#include <fenv.h>
#include "fenv_inlines.h"
#include "libm_inlines.h"
#ifdef __sparcv9
#define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
#define FPREG(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_dregs[(X>>1)| \
((X&1)<<4)]
#else
#include <sys/procfs.h>
#define FPxreg(X) &((prxregset_t*)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfr.pr_regs[X]
#define FPreg(X) &uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[X]
#define FPREG(X) ((X & 1)? FPxreg(X - 1) : FPreg(X))
#endif
#include "fex_handler.h"
static enum fp_class_type
my_fp_classl(long double *a)
{
int msw = *(int*)a & ~0x80000000;
if (msw >= 0x7fff0000) {
if (((msw & 0xffff) | *(1+(int*)a) | *(2+(int*)a) | *(3+(int*)a)) == 0)
return fp_infinity;
else if (msw & 0x8000)
return fp_quiet;
else
return fp_signaling;
} else if (msw < 0x10000) {
if ((msw | *(1+(int*)a) | *(2+(int*)a) | *(3+(int*)a)) == 0)
return fp_zero;
else
return fp_subnormal;
} else
return fp_normal;
}
enum fex_exception
__fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
{
unsigned instr, opf, rs1, rs2;
enum fp_class_type t1, t2;
instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
opf = (instr >> 5) & 0x1ff;
rs1 = (instr >> 14) & 0x1f;
rs2 = instr & 0x1f;
switch (opf & 3) {
case 1:
t1 = fp_classf(*(float*)FPreg(rs1));
t2 = fp_classf(*(float*)FPreg(rs2));
break;
case 2:
t1 = fp_class(*(double*)FPREG(rs1));
t2 = fp_class(*(double*)FPREG(rs2));
break;
case 3:
t1 = my_fp_classl((long double*)FPREG(rs1));
t2 = my_fp_classl((long double*)FPREG(rs2));
break;
default:
return (enum fex_exception) -1;
}
if (t2 == fp_signaling)
return fex_inv_snan;
switch ((instr >> 19) & 0x183f) {
case 0x1034:
switch (opf & 0x1fc) {
case 0x40:
case 0x44:
if (t1 == fp_signaling)
return fex_inv_snan;
else
return fex_inv_isi;
case 0x48:
case 0x68:
case 0x6c:
if (t1 == fp_signaling)
return fex_inv_snan;
else
return fex_inv_zmi;
case 0x4c:
if (t1 == fp_signaling)
return fex_inv_snan;
else if (t1 == fp_zero)
return fex_inv_zdz;
else
return fex_inv_idi;
case 0x28:
return fex_inv_sqrt;
case 0x80:
case 0xd0:
return fex_inv_int;
}
break;
case 0x1035:
if (t1 == fp_signaling)
return fex_inv_snan;
else
return fex_inv_cmp;
}
return (enum fex_exception) -1;
}
#ifdef __sparcv9
extern void _Qp_sqrt(long double *, const long double *);
#else
extern long double _Q_sqrt(long double);
#endif
void
__fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
{
unsigned long fsr;
unsigned instr, opf, rs1, rs2;
volatile int c;
instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
opf = (instr >> 5) & 0x1ff;
rs1 = (instr >> 14) & 0x1f;
rs2 = instr & 0x1f;
switch (opf & 3) {
case 0:
info->op1.type = fex_nodata;
if (opf & 0x40) {
info->op2.type = fex_int;
info->op2.val.i = *(int*)FPreg(rs2);
}
else {
info->op2.type = fex_llong;
info->op2.val.l = *(long long*)FPREG(rs2);
}
break;
case 1:
info->op1.type = info->op2.type = fex_float;
info->op1.val.f = *(float*)FPreg(rs1);
info->op2.val.f = *(float*)FPreg(rs2);
break;
case 2:
info->op1.type = info->op2.type = fex_double;
info->op1.val.d = *(double*)FPREG(rs1);
info->op2.val.d = *(double*)FPREG(rs2);
break;
case 3:
info->op1.type = info->op2.type = fex_ldouble;
info->op1.val.q = *(long double*)FPREG(rs1);
info->op2.val.q = *(long double*)FPREG(rs2);
break;
}
info->op = fex_other;
info->res.type = fex_nodata;
switch ((instr >> 19) & 0x183f) {
case 0x1035:
info->op = fex_cmp;
switch (opf) {
case 0x51:
c = (info->op1.val.f == info->op2.val.f);
break;
case 0x52:
c = (info->op1.val.d == info->op2.val.d);
break;
case 0x53:
c = (info->op1.val.q == info->op2.val.q);
break;
case 0x55:
c = (info->op1.val.f < info->op2.val.f);
break;
case 0x56:
c = (info->op1.val.d < info->op2.val.d);
break;
case 0x57:
c = (info->op1.val.q < info->op2.val.q);
break;
}
break;
case 0x1034:
switch (opf) {
case 0x41:
info->op = fex_add;
info->res.type = fex_float;
info->res.val.f = info->op1.val.f + info->op2.val.f;
break;
case 0x42:
info->op = fex_add;
info->res.type = fex_double;
info->res.val.d = info->op1.val.d + info->op2.val.d;
break;
case 0x43:
info->op = fex_add;
info->res.type = fex_ldouble;
info->res.val.q = info->op1.val.q + info->op2.val.q;
break;
case 0x45:
info->op = fex_sub;
info->res.type = fex_float;
info->res.val.f = info->op1.val.f - info->op2.val.f;
break;
case 0x46:
info->op = fex_sub;
info->res.type = fex_double;
info->res.val.d = info->op1.val.d - info->op2.val.d;
break;
case 0x47:
info->op = fex_sub;
info->res.type = fex_ldouble;
info->res.val.q = info->op1.val.q - info->op2.val.q;
break;
case 0x49:
info->op = fex_mul;
info->res.type = fex_float;
info->res.val.f = info->op1.val.f * info->op2.val.f;
break;
case 0x4a:
info->op = fex_mul;
info->res.type = fex_double;
info->res.val.d = info->op1.val.d * info->op2.val.d;
break;
case 0x4b:
info->op = fex_mul;
info->res.type = fex_ldouble;
info->res.val.q = info->op1.val.q * info->op2.val.q;
break;
case 0x69:
info->op = fex_mul;
info->res.type = fex_double;
info->res.val.d = (double)info->op1.val.f * (double)info->op2.val.f;
break;
case 0x6e:
info->op = fex_mul;
info->res.type = fex_ldouble;
info->res.val.q = (long double)info->op1.val.d *
(long double)info->op2.val.d;
break;
case 0x4d:
info->op = fex_div;
info->res.type = fex_float;
info->res.val.f = info->op1.val.f / info->op2.val.f;
break;
case 0x4e:
info->op = fex_div;
info->res.type = fex_double;
info->res.val.d = info->op1.val.d / info->op2.val.d;
break;
case 0x4f:
info->op = fex_div;
info->res.type = fex_ldouble;
info->res.val.q = info->op1.val.q / info->op2.val.q;
break;
case 0x29:
info->op = fex_sqrt;
info->op1 = info->op2;
info->op2.type = fex_nodata;
info->res.type = fex_float;
info->res.val.f = sqrtf(info->op1.val.f);
break;
case 0x2a:
info->op = fex_sqrt;
info->op1 = info->op2;
info->op2.type = fex_nodata;
info->res.type = fex_double;
info->res.val.d = sqrt(info->op1.val.d);
break;
case 0x2b:
info->op = fex_sqrt;
info->op1 = info->op2;
info->op2.type = fex_nodata;
info->res.type = fex_ldouble;
#ifdef __sparcv9
_Qp_sqrt(&info->res.val.q, &info->op1.val.q);
#else
info->res.val.q = _Q_sqrt(info->op1.val.q);
#endif
break;
default:
info->op = fex_cnvt;
info->op1 = info->op2;
info->op2.type = fex_nodata;
switch (opf) {
case 0xd1:
info->res.type = fex_int;
info->res.val.i = (int) info->op1.val.f;
break;
case 0xd2:
info->res.type = fex_int;
info->res.val.i = (int) info->op1.val.d;
break;
case 0xd3:
info->res.type = fex_int;
info->res.val.i = (int) info->op1.val.q;
break;
case 0x81:
info->res.type = fex_llong;
info->res.val.l = (long long) info->op1.val.f;
break;
case 0x82:
info->res.type = fex_llong;
info->res.val.l = (long long) info->op1.val.d;
break;
case 0x83:
info->res.type = fex_llong;
info->res.val.l = (long long) info->op1.val.q;
break;
case 0xc4:
info->res.type = fex_float;
info->res.val.f = (float) info->op1.val.i;
break;
case 0x84:
info->res.type = fex_float;
info->res.val.f = (float) info->op1.val.l;
break;
case 0x88:
info->res.type = fex_double;
info->res.val.d = (double) info->op1.val.l;
break;
case 0xc6:
info->res.type = fex_float;
info->res.val.f = (float) info->op1.val.d;
break;
case 0xc7:
info->res.type = fex_float;
info->res.val.f = (float) info->op1.val.q;
break;
case 0xc9:
info->res.type = fex_double;
info->res.val.d = (double) info->op1.val.f;
break;
case 0xcb:
info->res.type = fex_double;
info->res.val.d = (double) info->op1.val.q;
break;
case 0xcd:
info->res.type = fex_ldouble;
info->res.val.q = (long double) info->op1.val.f;
break;
case 0xce:
info->res.type = fex_ldouble;
info->res.val.q = (long double) info->op1.val.d;
break;
}
}
break;
}
__fenv_getfsr(&fsr);
info->flags = (int)__fenv_get_ex(fsr);
__fenv_set_ex(fsr, 0);
__fenv_setfsr(&fsr);
}
void
__fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
{
unsigned instr, opf, rs1, rs2, rd;
long double qscl;
double dscl;
float fscl;
instr = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr;
opf = (instr >> 5) & 0x1ff;
rs1 = (instr >> 14) & 0x1f;
rs2 = instr & 0x1f;
rd = (instr >> 25) & 0x1f;
if (((instr >> 19) & 0x183f) == 0x1035) {
if (rd == 0)
uap->uc_mcontext.fpregs.fpu_fsr |= 0xc00;
else {
#ifdef __sparcv9
uap->uc_mcontext.fpregs.fpu_fsr |= (3l << ((rd << 1) + 30));
#else
((prxregset_t*)uap->uc_mcontext.xrs.xrs_ptr)->pr_un.pr_v8p.pr_xfsr |= (3 << ((rd - 1) << 1));
#endif
}
return;
}
if (info->res.type == fex_nodata) {
switch (sip->si_code) {
case FPE_FLTOVF:
fscl = 1.262177448e-29f;
dscl = 6.441148769597133308e-232;
qscl = 8.778357852076208839765066529179033145e-3700l;
break;
case FPE_FLTUND:
fscl = 7.922816251e+28f;
dscl = 1.552518092300708935e+231;
qscl = 1.139165225263043370845938579315932009e+3699l;
break;
default:
(void) __fex_get_op(sip, uap, info);
if (info->res.type != fex_nodata)
goto stuff;
return;
}
switch (opf & 3) {
case 1:
info->op1.val.f = *(float*)FPreg(rs1);
info->op2.val.f = *(float*)FPreg(rs2);
break;
case 2:
info->op1.val.d = *(double*)FPREG(rs1);
info->op2.val.d = *(double*)FPREG(rs2);
break;
case 3:
info->op1.val.q = *(long double*)FPREG(rs1);
info->op2.val.q = *(long double*)FPREG(rs2);
break;
}
switch (opf) {
case 0x41:
info->res.type = fex_float;
info->res.val.f = fscl * (fscl * info->op1.val.f +
fscl * info->op2.val.f);
break;
case 0x42:
info->res.type = fex_double;
info->res.val.d = dscl * (dscl * info->op1.val.d +
dscl * info->op2.val.d);
break;
case 0x43:
info->res.type = fex_ldouble;
info->res.val.q = qscl * (qscl * info->op1.val.q +
qscl * info->op2.val.q);
break;
case 0x45:
info->res.type = fex_float;
info->res.val.f = fscl * (fscl * info->op1.val.f -
fscl * info->op2.val.f);
break;
case 0x46:
info->res.type = fex_double;
info->res.val.d = dscl * (dscl * info->op1.val.d -
dscl * info->op2.val.d);
break;
case 0x47:
info->res.type = fex_ldouble;
info->res.val.q = qscl * (qscl * info->op1.val.q -
qscl * info->op2.val.q);
break;
case 0x49:
info->res.type = fex_float;
info->res.val.f = (fscl * info->op1.val.f) *
(fscl * info->op2.val.f);
break;
case 0x4a:
info->res.type = fex_double;
info->res.val.d = (dscl * info->op1.val.d) *
(dscl * info->op2.val.d);
break;
case 0x4b:
info->res.type = fex_ldouble;
info->res.val.q = (qscl * info->op1.val.q) *
(qscl * info->op2.val.q);
break;
case 0x4d:
info->res.type = fex_float;
info->res.val.f = (fscl * info->op1.val.f) /
(info->op2.val.f / fscl);
break;
case 0x4e:
info->res.type = fex_double;
info->res.val.d = (dscl * info->op1.val.d) /
(info->op2.val.d / dscl);
break;
case 0x4f:
info->res.type = fex_ldouble;
info->res.val.q = (qscl * info->op1.val.q) /
(info->op2.val.q / qscl);
break;
case 0xc6:
info->res.type = fex_float;
info->res.val.f = (float) (fscl * (fscl * info->op1.val.d));
break;
case 0xc7:
info->res.type = fex_float;
info->res.val.f = (float) (fscl * (fscl * info->op1.val.q));
break;
case 0xcb:
info->res.type = fex_double;
info->res.val.d = (double) (dscl * (dscl * info->op1.val.q));
break;
}
if (info->res.type == fex_nodata)
return;
}
stuff:
if (opf & 0x80) {
if (opf & 0x10) {
switch (info->res.type) {
case fex_llong:
info->res.val.i = (int) info->res.val.l;
break;
case fex_float:
info->res.val.i = (int) info->res.val.f;
break;
case fex_double:
info->res.val.i = (int) info->res.val.d;
break;
case fex_ldouble:
info->res.val.i = (int) info->res.val.q;
break;
default:
break;
}
*(int*)FPreg(rd) = info->res.val.i;
return;
}
switch (opf & 0xc) {
case 0:
switch (info->res.type) {
case fex_int:
info->res.val.l = (long long) info->res.val.i;
break;
case fex_float:
info->res.val.l = (long long) info->res.val.f;
break;
case fex_double:
info->res.val.l = (long long) info->res.val.d;
break;
case fex_ldouble:
info->res.val.l = (long long) info->res.val.q;
break;
default:
break;
}
*(long long*)FPREG(rd) = info->res.val.l;
break;
case 0x4:
switch (info->res.type) {
case fex_int:
info->res.val.f = (float) info->res.val.i;
break;
case fex_llong:
info->res.val.f = (float) info->res.val.l;
break;
case fex_double:
info->res.val.f = (float) info->res.val.d;
break;
case fex_ldouble:
info->res.val.f = (float) info->res.val.q;
break;
default:
break;
}
*(float*)FPreg(rd) = info->res.val.f;
break;
case 0x8:
switch (info->res.type) {
case fex_int:
info->res.val.d = (double) info->res.val.i;
break;
case fex_llong:
info->res.val.d = (double) info->res.val.l;
break;
case fex_float:
info->res.val.d = (double) info->res.val.f;
break;
case fex_ldouble:
info->res.val.d = (double) info->res.val.q;
break;
default:
break;
}
*(double*)FPREG(rd) = info->res.val.d;
break;
case 0xc:
switch (info->res.type) {
case fex_int:
info->res.val.q = (long double) info->res.val.i;
break;
case fex_llong:
info->res.val.q = (long double) info->res.val.l;
break;
case fex_float:
info->res.val.q = (long double) info->res.val.f;
break;
case fex_double:
info->res.val.q = (long double) info->res.val.d;
break;
default:
break;
}
*(long double*)FPREG(rd) = info->res.val.q;
break;
}
return;
}
if ((opf & 0xf0) == 0x60) {
switch (opf & 0xc0) {
case 0x8:
switch (info->res.type) {
case fex_int:
info->res.val.d = (double) info->res.val.i;
break;
case fex_llong:
info->res.val.d = (double) info->res.val.l;
break;
case fex_float:
info->res.val.d = (double) info->res.val.f;
break;
case fex_ldouble:
info->res.val.d = (double) info->res.val.q;
break;
default:
break;
}
*(double*)FPREG(rd) = info->res.val.d;
break;
case 0xc:
switch (info->res.type) {
case fex_int:
info->res.val.q = (long double) info->res.val.i;
break;
case fex_llong:
info->res.val.q = (long double) info->res.val.l;
break;
case fex_float:
info->res.val.q = (long double) info->res.val.f;
break;
case fex_double:
info->res.val.q = (long double) info->res.val.d;
break;
default:
break;
}
*(long double*)FPREG(rd) = info->res.val.q;
break;
}
return;
}
switch (opf & 3) {
case 1:
switch (info->res.type) {
case fex_int:
info->res.val.f = (float) info->res.val.i;
break;
case fex_llong:
info->res.val.f = (float) info->res.val.l;
break;
case fex_double:
info->res.val.f = (float) info->res.val.d;
break;
case fex_ldouble:
info->res.val.f = (float) info->res.val.q;
break;
default:
break;
}
*(float*)FPreg(rd) = info->res.val.f;
break;
case 2:
switch (info->res.type) {
case fex_int:
info->res.val.d = (double) info->res.val.i;
break;
case fex_llong:
info->res.val.d = (double) info->res.val.l;
break;
case fex_float:
info->res.val.d = (double) info->res.val.f;
break;
case fex_ldouble:
info->res.val.d = (double) info->res.val.q;
break;
default:
break;
}
*(double*)FPREG(rd) = info->res.val.d;
break;
case 3:
switch (info->res.type) {
case fex_int:
info->res.val.q = (long double) info->res.val.i;
break;
case fex_llong:
info->res.val.q = (long double) info->res.val.l;
break;
case fex_float:
info->res.val.q = (long double) info->res.val.f;
break;
case fex_double:
info->res.val.q = (long double) info->res.val.d;
break;
default:
break;
}
*(long double*)FPREG(rd) = info->res.val.q;
break;
}
}
#endif