__fex_i386.c revision 61679b0b6826b0ae7e3f751acd91412fcfa45d1e
/*
* 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 <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;
int *
{
int *p;
if (thr_main())
return &accrued;
else {
p = NULL;
if (thr_getspecific(accrued_key, (void **)&p) != 0 &&
return NULL;
}
if (!p) {
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)
{
/* clear reserved bits for no particularly good reason */
cwsw &= ~0xe0c00000u;
if (test_sse_hw) {
/* pick up exception flags (excluding denormal operand
flag) from mxcsr */
}
cwsw |= *__fex_accrued();
}
void
__fenv_setfsr(const unsigned long *fsr)
{
int te;
/* save accrued exception flags corresponding to enabled exceptions */
if (test_sse_hw) {
/* propagate rounding direction, masks, and exception flags
(excluding denormal operand mask and flag) to mxcsr */
}
}
/* Offsets into the fp environment save area (assumes 32-bit protected mode) */
#define CW 0 /* control word */
/* 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
{
#if defined(__amd64)
#else
#endif
/* store 0 for stack fault, FPE_FLTINV for IEEE invalid op */
else
}
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) {
return (((*(1+(int*)x) | *(int*)x) == 0)?
fp_zero : fp_subnormal);
}
return ((*(1+(int*)x) < 0)? fp_normal :
}
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
}
/*
* Determine which type of invalid operation exception occurred
*/
enum fex_exception
{
unsigned op;
unsigned long ea;
/* get the opcode and data address */
#if defined(__amd64)
#else
#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 */
if (t1 == fp_signaling)
return fex_inv_snan;
return (enum fex_exception) -1;
/* determine the class of the second operand if there is one */
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)
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)
break;
case 0x000:
case 0x020:
case 0x040:
case 0x060:
case 0x080:
case 0x0a0:
/* single precision memory operand */
if (!ea)
return (enum fex_exception) -1;
break;
case 0x400:
case 0x420:
case 0x440:
case 0x460:
case 0x480:
case 0x4a0:
/* double precision memory operand */
if (!ea)
return (enum fex_exception) -1;
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:
}
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 */
break;
}
break;
}
/* see if the second op is snan */
if (t2 == fp_signaling)
return fex_inv_snan;
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 */
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 */
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 */
return fex_inv_zdz;
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 */
return fex_inv_cmp;
break;
case 0x1e0:
/* ftst */
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
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
{
long double op2v, x;
unsigned long ea;
volatile int c;
/* get the exception type, status word, opcode, and data address */
#if defined(__amd64)
#else
#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 */
switch (op & 0x7f8) {
/* load instructions */
case 0x100:
case 0x140:
case 0x180:
if (!ea) {
return;
}
goto done;
case 0x500:
case 0x540:
case 0x580:
if (!ea) {
return;
}
goto done;
/* store instructions */
case 0x110:
case 0x118:
case 0x150:
case 0x158:
case 0x190:
case 0x198:
/* inexact, stack popped */
if (!ea) {
return;
}
return;
}
goto done;
case 0x310:
case 0x318:
case 0x350:
case 0x358:
case 0x390:
case 0x398:
/* inexact, stack popped */
if (!ea) {
return;
}
return;
}
goto done;
case 0x510:
case 0x518:
case 0x550:
case 0x558:
case 0x590:
case 0x598:
/* inexact, stack popped */
if (!ea) {
return;
}
return;
}
goto done;
case 0x710:
case 0x718:
case 0x750:
case 0x758:
case 0x790:
case 0x798:
/* inexact, stack popped */
if (!ea) {
return;
}
return;
}
goto done;
case 0x730:
case 0x770:
case 0x7b0:
/* fbstp; don't bother */
return;
case 0x738:
case 0x778:
case 0x7b8:
if (ex == FPE_FLTRES) {
/* inexact, stack popped */
if (!ea) {
return;
}
return;
}
goto done;
}
/* all other ops (except compares) have destinations on the stack
so overflow, underflow, and inexact will stomp their operands */
/* find the trapped result */
switch (op & 0x7f8) {
case 0x1f0:
/* fptan pushes 1.0 afterward, so result is in st(1) */
break;
case 0x4c0:
case 0x4c8:
case 0x4e0:
case 0x4e8:
case 0x4f0:
case 0x4f8:
break;
case 0x6c0:
case 0x6c8:
case 0x6e0:
case 0x6e8:
case 0x6f0:
case 0x6f8:
/* stack was popped afterward */
break;
default:
}
/* reconstruct default untrapped result */
if (ex == FPE_FLTOVF) {
/* generate an overflow with the sign of the result */
x = two12288;
cwsw &= ~FE_ALL_EXCEPT;
}
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 */
else
cwsw &= ~FE_ALL_EXCEPT;
}
else
/* 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 */
return;
case 0x1fa: /* fsqrt */
return;
}
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:
break;
case 0x08:
break;
case 0x20:
case 0x28:
break;
case 0x30:
case 0x38:
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 */
/* oddball instructions */
switch (op) {
case 0x1e4: /* ftst */
goto done;
case 0x1f0: /* f2xm1 */
goto done;
case 0x1f1: /* fyl2x */
goto done;
case 0x1f2: /* fptan */
goto done;
case 0x1f3: /* fpatan */
goto done;
case 0x1f4: /* fxtract */
goto done;
case 0x1f5: /* fprem1 */
goto done;
case 0x1f8: /* fprem */
goto done;
case 0x1f9: /* fyl2xp1 */
goto done;
case 0x1fa: /* fsqrt */
goto done;
case 0x1fb: /* fsincos */
goto done;
case 0x1fc: /* frndint */
goto done;
case 0x1fd: /* fscale */
goto done;
case 0x1fe: /* fsin */
goto done;
case 0x1ff: /* fcos */
goto done;
case 0x2e9: /* fucompp */
goto done;
}
/* fucom[p], fcomi[p], fucomi[p] */
switch (op & 0x7f8) {
case 0x3e8:
case 0x5e0:
case 0x5e8:
case 0x7e8: /* unordered compares */
goto done;
case 0x3f0:
case 0x7f0: /* ordered compares */
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) {
return;
}
break;
case 0x0c0:
break;
case 0x200:
case 0x240:
case 0x280:
if (!ea) {
return;
}
break;
case 0x400:
case 0x440:
case 0x480:
if (!ea) {
return;
}
break;
case 0x4c0:
case 0x6c0:
break;
case 0x600:
case 0x640:
case 0x680:
if (!ea) {
return;
}
break;
default:
return;
}
/* distinguish different operations in the group */
switch (op & 0x38) {
case 0x00:
break;
case 0x08:
break;
case 0x10:
case 0x18:
break;
case 0x20:
break;
case 0x28:
break;
case 0x30:
break;
case 0x38:
break;
default:
return;
}
done:
cwsw &= ~FE_ALL_EXCEPT;
}
/* pop the saved stack */
{
unsigned top;
#if defined(__amd64)
& 0xe;
| (top << 10);
#else
& 0xe;
| (top << 10);
#endif
}
/* push x onto the saved stack */
{
unsigned top;
#if defined(__amd64)
& 0xe;
| (top << 10);
#else
& 0xe;
| (top << 10);
#endif
}
/* scale factors for exponent wrapping */
static const float
static const double
/*
* Store the specified result; if no result is given but the exception
* is underflow or overflow, use the default trapped result
*/
void
{
/* get the exception type, opcode, and data address */
#if defined(__amd64)
#else
#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)
#else
#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)
#else
#endif
return;
case 0x2e8:
case 0x6d8:
/* f[u]compp */
#if defined(__amd64)
#else
#endif
return;
case 0x1e0:
#if defined(__amd64)
#else
#endif
return;
}
break;
case 0x3e8:
case 0x3f0:
/* f[u]comi */
#if defined(__amd64)
#else
#endif
return;
case 0x7e8:
case 0x7f0:
/* f[u]comip */
#if defined(__amd64)
#else
#endif
return;
}
/* if there is no result available and the exception is overflow
or underflow, use the wrapped result */
if (r.type == fex_nodata) {
/* 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)
else
if ((op & 8) != 0)
break;
case 0x510:
case 0x518:
case 0x550:
case 0x558:
case 0x590:
case 0x598:
if (!ea)
return;
if (ex == FPE_FLTOVF)
else
if ((op & 8) != 0)
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:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
return;
case 0x310:
case 0x318:
case 0x350:
case 0x358:
case 0x390:
case 0x398:
if (!ea)
return;
switch (r.type) {
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
return;
case 0x510:
case 0x518:
case 0x550:
case 0x558:
case 0x590:
case 0x598:
if (!ea)
return;
switch (r.type) {
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
return;
case 0x710:
case 0x718:
case 0x750:
case 0x758:
case 0x790:
case 0x798:
if (!ea)
return;
switch (r.type) {
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
return;
case 0x730:
case 0x770:
case 0x7b0:
/* fbstp; don't bother */
return;
case 0x738:
case 0x778:
case 0x7b8:
if (!ea)
return;
switch (r.type) {
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
if (ex != FPE_FLTRES)
return;
}
/* for all other instructions, the result goes into a register */
switch (r.type) {
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
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)
return;
}
/* for all other instructions, if the exception is overflow,
underflow, or inexact, the stack has already been updated */
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)
return;
case 0x1f2: /* fpatan */
/* fptan pushes 1.0 afterward */
if (stack)
else {
}
return;
case 0x1f4: /* fxtract */
case 0x1fb: /* fsincos */
/* leave the supplied result in st */
if (stack)
else {
}
return;
}
/* all others leave the stack alone and the result in st */
return;
case 0x4c0:
case 0x4c8:
case 0x4e0:
case 0x4e8:
case 0x4f0:
case 0x4f8:
return;
case 0x6c0:
case 0x6c8:
case 0x6e0:
case 0x6e8:
case 0x6f0:
case 0x6f8:
/* stack is popped afterward */
if (stack)
else {
}
return;
default:
return;
}
}