/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 <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
/*
* The following variable lives in libc on Solaris 10, where it
* gets set to a nonzero value at startup time on systems with SSE.
*/
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);
/* clear reserved bits for no particularly good reason */
cwsw &= ~0xe0c00000u;
if (test_sse_hw) {
/* pick up exception flags (excluding denormal operand
flag) from mxcsr */
__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;
/* save accrued exception flags corresponding to enabled exceptions */
cwsw = (unsigned int)*fsr;
te = __fenv_get_te(cwsw);
*__fex_accrued() = cwsw & te;
cwsw = (cwsw & ~te) ^ 0x003f0000;
if (test_sse_hw) {
/* propagate rounding direction, masks, and exception flags
(excluding denormal operand mask and flag) to mxcsr */
__fenv_getmxcsr(&mxcsr);
mxcsr = (mxcsr & ~0x7ebd) | ((cwsw >> 13) & 0x6000) |
((cwsw >> 9) & 0x1e80) | (cwsw & 0x3d);
__fenv_setmxcsr(&mxcsr);
}
__fenv_setcwsw(&cwsw);
}
/* Offsets into the fp environment save area (assumes 32-bit protected mode) */
#define CW 0 /* control word */
#define SW 1 /* status word */
#define TW 2 /* tag word */
#define IP 3 /* instruction pointer */
#define OP 4 /* opcode */
#define EA 5 /* operand address */
/* macro for accessing fp registers in the save area */
#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
/*
* Fix sip->si_code; the Solaris x86 kernel can get it wrong
*/
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)))
/* store 0 for stack fault, FPE_FLTINV for IEEE invalid op */
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; /* pseudo-denormal */
return (((*(1+(int*)x) | *(int*)x) == 0)?
fp_zero : fp_subnormal);
}
return ((*(1+(int*)x) < 0)? fp_normal :
(enum fp_class_type) -1); /* unsupported format */
}
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; /* unsupported format */
}
/*
* Determine which type of invalid operation exception occurred
*/
enum fex_exception
__fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
{
unsigned op;
unsigned long ea;
enum fp_class_type t1, t2;
/* get the opcode and data address */
#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
/* if the instruction is fld, the source must be snan (it can't be
an unsupported format, since fldt doesn't raise any exceptions) */
switch (op & 0x7f8) {
case 0x100:
case 0x140:
case 0x180:
case 0x500:
case 0x540:
case 0x580:
return fex_inv_snan;
}
/* otherwise st is one of the operands; see if it's 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;
/* determine the class of the second operand if there is one */
t2 = fp_normal;
switch (op & 0x7e0) {
case 0x600:
case 0x620:
case 0x640:
case 0x660:
case 0x680:
case 0x6a0:
/* short memory operand */
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:
/* int memory operand */
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:
/* single precision memory operand */
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:
/* double precision memory operand */
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:
/* register operand determined by opcode */
switch (op & 0x7f8) {
case 0x3e0:
case 0x3f8:
case 0x5f0:
case 0x5f8:
case 0x7e0:
case 0x7f8:
/* weed out nonexistent opcodes */
break;
default:
t2 = my_fp_classl(&fpreg(uap, op & 7));
}
break;
case 0x1e0:
case 0x2e0:
/* special forms */
switch (op) {
case 0x1f1: /* fyl2x */
case 0x1f3: /* fpatan */
case 0x1f5: /* fprem1 */
case 0x1f8: /* fprem */
case 0x1f9: /* fyl2xp1 */
case 0x1fd: /* fscale */
case 0x2e9: /* fucompp */
t2 = my_fp_classl(&fpreg(uap, 1));
break;
}
break;
}
/* see if the second op is snan */
if (t2 == fp_signaling)
return fex_inv_snan;
else if (t2 == (enum fp_class_type) -1)
return (enum fex_exception) -1;
/* determine the type of operation */
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:
/* fadd, fsub, fsubr */
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:
/* fmul */
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:
/* fdiv */
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:
/* fsqrt, other special ops */
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:
/* fcom */
if (t1 == fp_quiet || t2 == fp_quiet)
return fex_inv_cmp;
break;
case 0x1e0:
/* ftst */
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:
/* fist, fbst */
return fex_inv_int;
}
return (enum fex_exception) -1;
}
/* scale factors for exponent unwrapping */
static const long double
two12288 = 1.139165225263043370845938579315932009e+3699l, /* 2^12288 */
twom12288 = 8.778357852076208839765066529179033145e-3700l, /* 2^-12288 */
twom12288mulp = 8.778357852076208839289190796475222545e-3700l;
/* (")*(1-2^-64) */
/* inline templates */
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);
/*
* Get the operands, generate the default untrapped result with
* exceptions, and set a code indicating the type of operation
*/
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;
/* get the exception type, status word, opcode, and data address */
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
/* initialize res to the default untrapped result and ex to the
corresponding flags (assume trapping is disabled and flags
are clear) */
/* single operand instructions */
info->op = fex_cnvt;
info->op2.type = fex_nodata;
switch (op & 0x7f8) {
/* load instructions */
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;
/* store instructions */
case 0x110:
case 0x118:
case 0x150:
case 0x158:
case 0x190:
case 0x198:
info->res.type = fex_float;
if (ex == FPE_FLTRES && (op & 8) != 0) {
/* inexact, stack popped */
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) {
/* inexact, stack popped */
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) {
/* inexact, stack popped */
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) {
/* inexact, stack popped */
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:
/* fbstp; don't bother */
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) {
/* inexact, stack popped */
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;
}
/* all other ops (except compares) have destinations on the stack
so overflow, underflow, and inexact will stomp their operands */
if (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES) {
/* find the trapped result */
info->op1.type = info->op2.type = fex_nodata;
info->res.type = fex_ldouble;
switch (op & 0x7f8) {
case 0x1f0:
/* fptan pushes 1.0 afterward, so result is in st(1) */
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:
/* stack was popped afterward */
info->res.val.q = fpreg(uap, (op - 1) & 7);
break;
default:
info->res.val.q = fpreg(uap, 0);
}
/* reconstruct default untrapped result */
if (ex == FPE_FLTOVF) {
/* generate an overflow with the sign of the result */
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) {
/* undo the scaling; we can't distinguish a chopped result
from an exact one without futzing around to trap all in-
exact exceptions so as to keep the flag clear, so we just
punt */
if (sw & 0x200) /* result was rounded up */
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;
/* determine the operation code */
switch (op) {
case 0x1f0: /* f2xm1 */
case 0x1f1: /* fyl2x */
case 0x1f2: /* fptan */
case 0x1f3: /* fpatan */
case 0x1f5: /* fprem1 */
case 0x1f8: /* fprem */
case 0x1f9: /* fyl2xp1 */
case 0x1fb: /* fsincos */
case 0x1fc: /* frndint */
case 0x1fd: /* fscale */
case 0x1fe: /* fsin */
case 0x1ff: /* fcos */
info->op = fex_other;
return;
case 0x1fa: /* fsqrt */
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;
}
/* for other exceptions, the operands are preserved, so we can
just emulate the operation with traps disabled */
/* one operand is always in st */
info->op1.type = fex_ldouble;
info->op1.val.q = fpreg(uap, 0);
/* oddball instructions */
info->op = fex_other;
switch (op) {
case 0x1e4: /* ftst */
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: /* f2xm1 */
info->res.type = fex_ldouble;
info->res.val.q = f2xm1(info->op1.val.q);
goto done;
case 0x1f1: /* fyl2x */
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: /* fptan */
info->res.type = fex_ldouble;
info->res.val.q = fptan(info->op1.val.q);
goto done;
case 0x1f3: /* fpatan */
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: /* fxtract */
info->res.type = fex_ldouble;
info->res.val.q = fxtract(info->op1.val.q);
goto done;
case 0x1f5: /* fprem1 */
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: /* fprem */
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: /* fyl2xp1 */
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: /* fsqrt */
info->op = fex_sqrt;
info->res.type = fex_ldouble;
info->res.val.q = fsqrt(info->op1.val.q);
goto done;
case 0x1fb: /* fsincos */
info->res.type = fex_ldouble;
info->res.val.q = fsincos(info->op1.val.q);
goto done;
case 0x1fc: /* frndint */
info->res.type = fex_ldouble;
info->res.val.q = frndint(info->op1.val.q);
goto done;
case 0x1fd: /* fscale */
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: /* fsin */
info->res.type = fex_ldouble;
info->res.val.q = fsin(info->op1.val.q);
goto done;
case 0x1ff: /* fcos */
info->res.type = fex_ldouble;
info->res.val.q = fcos(info->op1.val.q);
goto done;
case 0x2e9: /* fucompp */
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;
}
/* fucom[p], fcomi[p], fucomi[p] */
switch (op & 0x7f8) {
case 0x3e8:
case 0x5e0:
case 0x5e8:
case 0x7e8: /* unordered compares */
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: /* ordered compares */
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;
}
/* all other instructions come in groups of the form
fadd, fmul, fcom, fcomp, fsub, fsubr, fdiv, fdivr */
/* get the second operand */
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;
}
/* distinguish different operations in the group */
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);
}
/* pop the saved stack */
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
}
/* push x onto the saved stack */
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
}
/* scale factors for exponent wrapping */
static const float
fun = 7.922816251e+28f, /* 2^96 */
fov = 1.262177448e-29f; /* 2^-96 */
static const double
dun = 1.552518092300708935e+231, /* 2^768 */
dov = 6.441148769597133308e-232; /* 2^-768 */
/*
* Store the specified result; if no result is given but the exception
* is underflow or overflow, use the default trapped result
*/
void
__fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
{
fex_numeric_t r;
unsigned long ex, op, ea, stack;
/* get the exception type, opcode, and data address */
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
/* if the instruction is a compare, set the condition codes
to unordered and update the stack */
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:
/* f[u]com */
#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:
/* f[u]comp */
#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:
/* f[u]compp */
#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) { /* ftst */
#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:
/* f[u]comi */
#if defined(__amd64)
uap->uc_mcontext.gregs[REG_PS] |= 0x45;
#else
uap->uc_mcontext.gregs[EFL] |= 0x45;
#endif
return;
case 0x7e8:
case 0x7f0:
/* f[u]comip */
#if defined(__amd64)
uap->uc_mcontext.gregs[REG_PS] |= 0x45;
#else
uap->uc_mcontext.gregs[EFL] |= 0x45;
#endif
pop(uap);
return;
}
/* if there is no result available and the exception is overflow
or underflow, use the wrapped result */
r = info->res;
if (r.type == fex_nodata) {
if (ex == FPE_FLTOVF || ex == FPE_FLTUND) {
/* for store instructions, do the scaling and store */
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;
}
/* otherwise convert the supplied result to the correct type,
put it in the destination, and update the stack as need be */
/* store instructions */
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:
/* fbstp; don't bother */
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;
}
/* for all other instructions, the result goes into a register */
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;
}
/* for load instructions, push the result onto the stack */
switch (op & 0x7f8) {
case 0x100:
case 0x140:
case 0x180:
case 0x500:
case 0x540:
case 0x580:
if (ea)
push(r.val.q, uap);
return;
}
/* for all other instructions, if the exception is overflow,
underflow, or inexact, the stack has already been updated */
stack = (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES);
switch (op & 0x7f8) {
case 0x1f0: /* oddballs */
switch (op) {
case 0x1f1: /* fyl2x */
case 0x1f3: /* fpatan */
case 0x1f9: /* fyl2xp1 */
/* pop the stack, leaving the result in st */
if (!stack)
pop(uap);
fpreg(uap, 0) = r.val.q;
return;
case 0x1f2: /* fpatan */
/* fptan pushes 1.0 afterward */
if (stack)
fpreg(uap, 1) = r.val.q;
else {
fpreg(uap, 0) = r.val.q;
push(1.0L, uap);
}
return;
case 0x1f4: /* fxtract */
case 0x1fb: /* fsincos */
/* leave the supplied result in st */
if (stack)
fpreg(uap, 0) = r.val.q;
else {
fpreg(uap, 0) = 0.0; /* punt */
push(r.val.q, uap);
}
return;
}
/* all others leave the stack alone and the result in st */
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:
/* stack is popped afterward */
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;
}
}