/*
* 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 <ucontext.h>
#include <fenv.h>
#if defined(__SUNPRO_C)
#include <sunmath.h>
#else
#endif
#include "fex_handler.h"
#include "fenv_inlines.h"
#if !defined(REG_PC)
#endif
#if !defined(REG_PS)
#endif
#ifdef __amd64
#else
#endif
/*
* Support for SSE instructions
*/
/*
* Decode an SSE instruction. Fill in *inst and return the length of the
* instruction in bytes. Return 0 if the instruction is not recognized.
*/
int
{
unsigned char *ip;
char *addr;
i = 0;
/* look for pseudo-prefixes */
dbl = 0;
if (ip[i] == 0xF3) {
simd = 0;
i++;
} else if (ip[i] == 0x66) {
i++;
} else if (ip[i] == 0xF2) {
simd = 0;
i++;
}
/* look for AMD64 REX prefix */
rex = 0;
i++;
}
/* parse opcode */
if (ip[i++] != 0x0F)
return 0;
switch (ip[i++]) {
case 0x2A:
if (!simd)
break;
case 0x2C:
if (!simd)
break;
case 0x2D:
if (!simd)
break;
case 0x2E:
/* oddball: scalar instruction in a SIMD opcode group */
if (!simd)
return 0;
break;
case 0x2F:
/* oddball: scalar instruction in a SIMD opcode group */
if (!simd)
return 0;
break;
case 0x51:
break;
case 0x58:
break;
case 0x59:
break;
case 0x5A:
break;
case 0x5B:
if (dbl) {
if (simd)
else
return 0;
} else {
}
break;
case 0x5C:
break;
case 0x5D:
break;
case 0x5E:
break;
case 0x5F:
break;
case 0xC2:
break;
case 0xE6:
if (simd) {
if (dbl)
else
return 0;
} else {
}
break;
default:
return 0;
}
/* locate operands */
/* op1 is a gp register */
/* op1 is a mmx register */
#ifdef __amd64
#else
#endif
} else {
/* op1 is a xmm register */
}
/* op2 is a gp register */
/* op2 is a mmx register */
#ifdef __amd64
#else
#endif
} else {
/* op2 is a xmm register */
}
#ifdef __amd64
/* address of next instruction + offset */
r = i + 4;
r++;
#else
/* absolute address */
#endif
i += 4;
} else {
/* complex address */
/* parse sib byte */
/* start with absolute address */
i += 4;
} else {
/* start with base */
}
if (r != 4) {
/* add scaled index */
<< (sib >> 6);
}
} else {
}
/* add displacement, if any */
i += 4;
}
}
/* get the immediate operand */
}
return i;
}
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;
}
/*
* Inspect a scalar SSE instruction that incurred an invalid operation
* exception to determine which type of exception it was.
*/
static enum fex_exception
{
/* check op2 for signaling nan */
if (t2 == fp_signaling)
return fex_inv_snan;
/* eliminate all single-operand instructions */
case cvtsd2ss:
case cvtss2sd:
/* hmm, this shouldn't have happened */
return (enum fex_exception) -1;
case sqrtss:
case sqrtsd:
return fex_inv_sqrt;
case cvtss2si:
case cvtsd2si:
case cvttss2si:
case cvttsd2si:
case cvtss2siq:
case cvtsd2siq:
case cvttss2siq:
case cvttsd2siq:
return fex_inv_int;
default:
break;
}
/* check op1 for signaling nan */
if (t1 == fp_signaling)
return fex_inv_snan;
/* check two-operand instructions for other cases */
case cmpss:
case cmpsd:
case minss:
case minsd:
case maxss:
case maxsd:
case comiss:
case comisd:
return fex_inv_cmp;
case addss:
case addsd:
case subss:
case subsd:
return fex_inv_isi;
break;
case mulss:
case mulsd:
return fex_inv_zmi;
break;
case divss:
case divsd:
return fex_inv_zdz;
return fex_inv_idi;
default:
break;
}
return (enum fex_exception)-1;
}
/* inline templates */
extern void sse_cmpeqss(float *, float *, int *);
extern void sse_cmpltss(float *, float *, int *);
extern void sse_cmpless(float *, float *, int *);
extern void sse_cmpunordss(float *, float *, int *);
extern void sse_minss(float *, float *, float *);
extern void sse_maxss(float *, float *, float *);
extern void sse_addss(float *, float *, float *);
extern void sse_subss(float *, float *, float *);
extern void sse_mulss(float *, float *, float *);
extern void sse_divss(float *, float *, float *);
extern void sse_sqrtss(float *, float *);
extern void sse_ucomiss(float *, float *);
extern void sse_comiss(float *, float *);
extern void sse_cvtss2sd(float *, double *);
extern void sse_cvtsi2ss(int *, float *);
extern void sse_cvttss2si(float *, int *);
extern void sse_cvtss2si(float *, int *);
#ifdef __amd64
extern void sse_cvtsi2ssq(long long *, float *);
extern void sse_cvttss2siq(float *, long long *);
extern void sse_cvtss2siq(float *, long long *);
#endif
extern void sse_cmpeqsd(double *, double *, long long *);
extern void sse_cmpltsd(double *, double *, long long *);
extern void sse_cmplesd(double *, double *, long long *);
extern void sse_cmpunordsd(double *, double *, long long *);
extern void sse_minsd(double *, double *, double *);
extern void sse_maxsd(double *, double *, double *);
extern void sse_addsd(double *, double *, double *);
extern void sse_subsd(double *, double *, double *);
extern void sse_mulsd(double *, double *, double *);
extern void sse_divsd(double *, double *, double *);
extern void sse_sqrtsd(double *, double *);
extern void sse_ucomisd(double *, double *);
extern void sse_comisd(double *, double *);
extern void sse_cvtsd2ss(double *, float *);
extern void sse_cvtsi2sd(int *, double *);
extern void sse_cvttsd2si(double *, int *);
extern void sse_cvtsd2si(double *, int *);
#ifdef __amd64
extern void sse_cvtsi2sdq(long long *, double *);
extern void sse_cvttsd2siq(double *, long long *);
extern void sse_cvtsd2siq(double *, long long *);
#endif
/*
* Fill in *info with the operands, default untrapped result, and
* flags produced by a scalar SSE instruction, and return the type
* of trapped exception (if any). On entry, the mxcsr must have
* all exceptions masked and all flags clear. The same conditions
* will hold on exit.
*
* This routine does not work if the instruction specified by *inst
* is not a scalar instruction.
*/
enum fex_exception
{
/*
* Perform the operation with traps disabled and check the
* exception flags. If the underflow trap was enabled, also
* check for an exact subnormal result.
*/
subnorm = 0;
} else {
}
case cmpsd:
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
}
break;
case minsd:
break;
case maxsd:
break;
case addsd:
subnorm = 1;
break;
case subsd:
subnorm = 1;
break;
case mulsd:
subnorm = 1;
break;
case divsd:
subnorm = 1;
break;
case sqrtsd:
break;
case cvtsd2ss:
subnorm = 1;
break;
case cvtsi2sd:
break;
case cvttsd2si:
break;
case cvtsd2si:
break;
#ifdef __amd64
case cvtsi2sdq:
break;
case cvttsd2siq:
break;
case cvtsd2siq:
break;
#endif
case ucomisd:
break;
case comisd:
break;
default:
break;
}
} else {
} else {
}
case cmpss:
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
}
break;
case minss:
break;
case maxss:
break;
case addss:
subnorm = 1;
break;
case subss:
subnorm = 1;
break;
case mulss:
subnorm = 1;
break;
case divss:
subnorm = 1;
break;
case sqrtss:
break;
case cvtss2sd:
break;
case cvtsi2ss:
break;
case cvttss2si:
break;
case cvtss2si:
break;
#ifdef __amd64
case cvtsi2ssq:
break;
case cvttss2siq:
break;
case cvtss2siq:
break;
#endif
case ucomiss:
break;
case comiss:
break;
default:
break;
}
}
/* determine which exception would have been trapped */
>> 7) & 0x3d;
if (e & FE_INVALID)
return __fex_get_sse_invalid_type(inst);
if (e & FE_DIVBYZERO)
return fex_division;
if (e & FE_OVERFLOW)
return fex_overflow;
return fex_underflow;
if (e & FE_INEXACT)
return fex_inexact;
return (enum fex_exception)-1;
}
/*
* Emulate a SIMD SSE instruction to determine which exceptions occur
* in each part. For i = 0, 1, 2, and 3, set e[i] to indicate the
* trapped exception that would occur if the i-th part of the SIMD
* instruction were executed in isolation; set e[i] to -1 if no
* trapped exception would occur in this part. Also fill in info[i]
* with the corresponding operands, default untrapped result, and
* flags.
*
* This routine does not work if the instruction specified by *inst
* is not a SIMD instruction.
*/
void
{
int i;
e[0] = e[1] = e[2] = e[3] = -1;
/* perform each part of the SIMD operation */
case cmpps:
for (i = 0; i < 4; i++) {
}
break;
case minps:
for (i = 0; i < 4; i++) {
}
break;
case maxps:
for (i = 0; i < 4; i++) {
}
break;
case addps:
for (i = 0; i < 4; i++) {
}
break;
case subps:
for (i = 0; i < 4; i++) {
}
break;
case mulps:
for (i = 0; i < 4; i++) {
}
break;
case divps:
for (i = 0; i < 4; i++) {
}
break;
case sqrtps:
for (i = 0; i < 4; i++) {
}
break;
case cvtdq2ps:
for (i = 0; i < 4; i++) {
}
break;
case cvttps2dq:
for (i = 0; i < 4; i++) {
}
break;
case cvtps2dq:
for (i = 0; i < 4; i++) {
}
break;
case cvtpi2ps:
for (i = 0; i < 2; i++) {
}
break;
case cvttps2pi:
for (i = 0; i < 2; i++) {
}
break;
case cvtps2pi:
for (i = 0; i < 2; i++) {
}
break;
case cmppd:
for (i = 0; i < 2; i++) {
}
break;
case minpd:
for (i = 0; i < 2; i++) {
}
break;
case maxpd:
for (i = 0; i < 2; i++) {
}
break;
case addpd:
for (i = 0; i < 2; i++) {
}
break;
case subpd:
for (i = 0; i < 2; i++) {
}
break;
case mulpd:
for (i = 0; i < 2; i++) {
}
break;
case divpd:
for (i = 0; i < 2; i++) {
}
break;
case sqrtpd:
for (i = 0; i < 2; i++) {
}
break;
case cvtpi2pd:
case cvtdq2pd:
for (i = 0; i < 2; i++) {
}
break;
case cvttpd2pi:
case cvttpd2dq:
for (i = 0; i < 2; i++) {
}
break;
case cvtpd2pi:
case cvtpd2dq:
for (i = 0; i < 2; i++) {
}
break;
case cvtps2pd:
for (i = 0; i < 2; i++) {
}
break;
case cvtpd2ps:
for (i = 0; i < 2; i++) {
}
default:
break;
}
}
/*
* Store the result value from *info in the destination of the scalar
* SSE instruction specified by *inst. If no result is given but the
* exception is underflow or overflow, supply the default trapped result.
*
* This routine does not work if the instruction specified by *inst
* is not a scalar instruction.
*/
void
{
int i = 0;
long long l = 0L;;
/* for compares that write eflags, just set the flags
to indicate "unordered" */
return;
}
/* if info doesn't specify a result value, try to generate
the default trapped result */
/* set scale factors for exponent wrapping */
switch (e) {
case fex_overflow:
break;
case fex_underflow:
break;
default:
return;
goto stuff;
}
/* generate the wrapped result */
case addsd:
break;
case subsd:
break;
case mulsd:
break;
case divsd:
break;
default:
return;
}
} else {
case addss:
break;
case subss:
break;
case mulss:
break;
case divss:
break;
default:
return;
}
}
}
/* put the result in the destination */
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
} else {
case fex_int:
break;
case fex_llong:
break;
case fex_float:
break;
case fex_double:
break;
case fex_ldouble:
break;
default:
break;
}
}
}
/*
* Store the results from a SIMD instruction. For each i, store
* the result value from info[i] in the i-th part of the destination
* of the SIMD SSE instruction specified by *inst. If no result
* is given but the exception indicated by e[i] is underflow or
* overflow, supply the default trapped result.
*
* This routine does not work if the instruction specified by *inst
* is not a SIMD instruction.
*/
void
{
int i;
/* store each part */
case cmpps:
for (i = 0; i < 4; i++) {
}
break;
case minps:
for (i = 0; i < 4; i++) {
}
break;
case maxps:
for (i = 0; i < 4; i++) {
}
break;
case addps:
for (i = 0; i < 4; i++) {
}
break;
case subps:
for (i = 0; i < 4; i++) {
}
break;
case mulps:
for (i = 0; i < 4; i++) {
}
break;
case divps:
for (i = 0; i < 4; i++) {
}
break;
case sqrtps:
for (i = 0; i < 4; i++) {
}
break;
case cvtdq2ps:
for (i = 0; i < 4; i++) {
}
break;
case cvttps2dq:
for (i = 0; i < 4; i++) {
}
break;
case cvtps2dq:
for (i = 0; i < 4; i++) {
}
break;
case cvtpi2ps:
for (i = 0; i < 2; i++) {
}
break;
case cvttps2pi:
for (i = 0; i < 2; i++) {
}
break;
case cvtps2pi:
for (i = 0; i < 2; i++) {
}
break;
case cmppd:
for (i = 0; i < 2; i++) {
}
break;
case minpd:
for (i = 0; i < 2; i++) {
}
break;
case maxpd:
for (i = 0; i < 2; i++) {
}
break;
case addpd:
for (i = 0; i < 2; i++) {
}
break;
case subpd:
for (i = 0; i < 2; i++) {
}
break;
case mulpd:
for (i = 0; i < 2; i++) {
}
break;
case divpd:
for (i = 0; i < 2; i++) {
}
break;
case sqrtpd:
for (i = 0; i < 2; i++) {
}
break;
case cvtpi2pd:
case cvtdq2pd:
for (i = 0; i < 2; i++) {
}
break;
case cvttpd2pi:
case cvttpd2dq:
for (i = 0; i < 2; i++) {
}
/* for cvttpd2dq, zero the high 64 bits of the destination */
break;
case cvtpd2pi:
case cvtpd2dq:
for (i = 0; i < 2; i++) {
}
/* for cvtpd2dq, zero the high 64 bits of the destination */
break;
case cvtps2pd:
for (i = 0; i < 2; i++) {
}
break;
case cvtpd2ps:
for (i = 0; i < 2; i++) {
}
/* zero the high 64 bits of the destination */
default:
break;
}
}