aboutsummaryrefslogtreecommitdiff
path: root/sim/common/sim-fpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/common/sim-fpu.c')
-rw-r--r--sim/common/sim-fpu.c601
1 files changed, 563 insertions, 38 deletions
diff --git a/sim/common/sim-fpu.c b/sim/common/sim-fpu.c
index 66fe5d1..d2b9266 100644
--- a/sim/common/sim-fpu.c
+++ b/sim/common/sim-fpu.c
@@ -1,5 +1,5 @@
/* Simulator Floating-point support.
- Copyright (C) 1997 Free Software Foundation, Inc.
+ Copyright (C) 1994, 1997 Free Software Foundation, Inc.
Contributed by Cygnus Support.
This file is part of GDB, the GNU debugger.
@@ -20,23 +20,412 @@ with this program; if not, write to the Free Software Foundation, Inc.,
-#ifndef _SIM_FPU_C_
-#define _SIM_FPU_C_
+#ifndef SIM_FPU_C
+#define SIM_FPU_C
#include "sim-main.h"
#include "sim-fpu.h"
+#include "sim-assert.h"
#include <math.h>
+/* Floating point number is <SIGN:1><EXP:EXPBITS><FRAC:FRACBITS> */
+
+#define SP_NGARDS 7L
+#define SP_GARDROUND 0x3f
+#define SP_GARDMASK 0x7f
+#define SP_GARDMSB 0x40
+#define SP_EXPBITS 8
+#define SP_EXPBIAS 127
+#define SP_FRACBITS 23
+#define SP_EXPMAX (0xff)
+#define SP_QUIET_NAN 0x100000L
+#define SP_FRAC_NBITS 32
+#define SP_FRACHIGH 0x80000000L
+#define SP_FRACHIGH2 0xc0000000L
+
+#define DP_NGARDS 8L
+#define DP_GARDROUND 0x7f
+#define DP_GARDMASK 0xff
+#define DP_GARDMSB 0x80
+#define DP_EXPBITS 11
+#define DP_EXPBIAS 1023
+#define DP_FRACBITS 52
+#define DP_EXPMAX (0x7ff)
+#define DP_QUIET_NAN 0x8000000000000LL
+#define DP_FRAC_NBITS 64
+#define DP_FRACHIGH 0x8000000000000000LL
+#define DP_FRACHIGH2 0xc000000000000000LL
+
+#define EXPMAX (is_double ? DP_EXPMAX : SP_EXPMAX)
+#define EXPBITS (is_double ? DP_EXPBITS : SP_EXPBITS)
+#define EXPBIAS (is_double ? DP_EXPBIAS : SP_EXPBIAS)
+#define FRACBITS (is_double ? DP_FRACBITS : SP_FRACBITS)
+#define NGARDS (is_double ? DP_NGARDS : (SP_NGARDS ))
+#define SIGNBIT (1LL << (EXPBITS + FRACBITS))
+#define FRAC_NBITS (is_double ? DP_FRAC_NBITS : SP_FRAC_NBITS)
+#define GARDMASK (is_double ? DP_GARDMASK : SP_GARDMASK)
+#define GARDMSB (is_double ? DP_GARDMSB : SP_GARDMSB)
+#define GARDROUND (is_double ? DP_GARDROUND : SP_GARDROUND)
+
+/* F_D_BITOFF is the number of bits offset between the MSB of the mantissa
+ of a float and of a double. Assumes there are only two float types.
+ (double::FRAC_BITS+double::NGARGS-(float::FRAC_BITS-float::NGARDS))
+ */
+#define F_D_BITOFF (is_double ? 0 : (52+8-(23+7)))
+
+
+#if 0
+#define (is_double ? DP_ : SP_)
+#endif
+
+#define NORMAL_EXPMIN (-(EXPBIAS)+1)
+
+#define IMPLICIT_1 (1LL<<(FRACBITS+NGARDS))
+#define IMPLICIT_2 (1LL<<(FRACBITS+1+NGARDS))
+
+#define MAX_SI_INT (is_double ? LSMASK64 (63) : LSMASK64 (31))
+#define MAX_USI_INT (is_double ? LSMASK64 (64) : LSMASK64 (32))
+
+
+typedef enum
+{
+ sim_fpu_class_snan,
+ sim_fpu_class_qnan,
+ sim_fpu_class_zero,
+ sim_fpu_class_number,
+ sim_fpu_class_infinity,
+} sim_fpu_class;
+
+typedef struct _sim_ufpu {
+ sim_fpu_class class;
+ int normal_exp;
+ int sign;
+ unsigned64 fraction;
+ union {
+ double d;
+ unsigned64 i;
+ } val;
+} sim_ufpu;
+
+
+STATIC_INLINE_SIM_FPU (unsigned64)
+pack_fpu (const sim_ufpu *src, int is_double)
+{
+ unsigned64 fraction;
+ unsigned64 exp;
+ int sign;
+
+ switch (src->class)
+ {
+ default:
+ /* create a NaN */
+ case sim_fpu_class_qnan:
+ case sim_fpu_class_snan:
+ sign = 1; /* fixme - always a qNaN */
+ exp = EXPMAX;
+ fraction = src->fraction;
+ break;
+ case sim_fpu_class_infinity:
+ sign = src->sign;
+ exp = EXPMAX;
+ fraction = 0;
+ break;
+ case sim_fpu_class_zero:
+ sign = src->sign;
+ exp = 0;
+ fraction = 0;
+ break;
+ case sim_fpu_class_number:
+ if (src->normal_exp < NORMAL_EXPMIN)
+ {
+ /* This number's exponent is too low to fit into the bits
+ available in the number, so we'll store 0 in the exponent and
+ shift the fraction to the right to make up for it. */
+
+ int shift = NORMAL_EXPMIN - src->normal_exp;
+
+ sign = src->sign;
+ exp = 0;
+
+ if (shift > (FRAC_NBITS - NGARDS))
+ {
+ /* No point shifting, since it's more that 64 out. */
+ fraction = 0;
+ }
+ else
+ {
+ /* Shift by the value */
+ fraction = src->fraction >> F_D_BITOFF;
+ fraction >>= shift;
+ fraction >>= NGARDS;
+ }
+ }
+ else if (src->normal_exp > EXPBIAS)
+ {
+ /* Infinity */
+ sign = src->sign;
+ exp = EXPMAX;
+ fraction = 0;
+ }
+ else
+ {
+ sign = src->sign;
+ exp = (src->normal_exp + EXPBIAS);
+ fraction = src->fraction >> F_D_BITOFF;
+ /* IF the gard bits are the all zero, but the first, then we're
+ half way between two numbers, choose the one which makes the
+ lsb of the answer 0. */
+ if ((fraction & GARDMASK) == GARDMSB)
+ {
+ if (fraction & (1 << NGARDS))
+ fraction += GARDROUND + 1;
+ }
+ else
+ {
+ /* Add a one to the guards to round up */
+ fraction += GARDROUND;
+ }
+ if (fraction >= IMPLICIT_2)
+ {
+ fraction >>= 1;
+ exp += 1;
+ }
+ fraction >>= NGARDS;
+ }
+ }
+
+ return ((sign ? SIGNBIT : 0)
+ | (exp << FRACBITS)
+ | LSMASKED64 (fraction, FRACBITS));
+}
+
+
+STATIC_INLINE_SIM_FPU (void)
+unpack_fpu (sim_ufpu *dst, unsigned64 s, int is_double)
+{
+ unsigned64 fraction = LSMASKED64 (s, FRACBITS);
+ unsigned exp = LSMASKED64 (s >> FRACBITS, EXPBITS);
+
+ dst->sign = (s & SIGNBIT) != 0;
+
+ if (exp == 0)
+ {
+ /* Hmm. Looks like 0 */
+ if (fraction == 0)
+ {
+ /* tastes like zero */
+ dst->class = sim_fpu_class_zero;
+ }
+ else
+ {
+ /* Zero exponent with non zero fraction - it's denormalized,
+ so there isn't a leading implicit one - we'll shift it so
+ it gets one. */
+ dst->normal_exp = exp - EXPBIAS + 1;
+ fraction <<= NGARDS;
+
+ dst->class = sim_fpu_class_number;
+ while (fraction < IMPLICIT_1)
+ {
+ fraction <<= 1;
+ dst->normal_exp--;
+ }
+ dst->fraction = fraction << F_D_BITOFF;
+ }
+ }
+ else if (exp == EXPMAX)
+ {
+ /* Huge exponent*/
+ if (fraction == 0)
+ {
+ /* Attached to a zero fraction - means infinity */
+ dst->class = sim_fpu_class_infinity;
+ }
+ else
+ {
+ /* Non zero fraction, means nan */
+ if (dst->sign)
+ {
+ dst->class = sim_fpu_class_snan;
+ }
+ else
+ {
+ dst->class = sim_fpu_class_qnan;
+ }
+ /* Keep the fraction part as the nan number */
+ dst->fraction = fraction << F_D_BITOFF;
+ }
+ }
+ else
+ {
+ /* Nothing strange about this number */
+ dst->normal_exp = exp - EXPBIAS;
+ dst->class = sim_fpu_class_number;
+ dst->fraction = ((fraction << NGARDS) | IMPLICIT_1) << F_D_BITOFF;
+ }
+
+ /* sanity checks */
+ dst->val.i = -1;
+ dst->val.i = pack_fpu (dst, 1);
+ {
+ if (is_double)
+ {
+ ASSERT (dst->val.i == s);
+ }
+ else
+ {
+ unsigned32 val = pack_fpu (dst, 0);
+ unsigned32 org = s;
+ ASSERT (val == org);
+ }
+ }
+}
+
+STATIC_INLINE_SIM_FPU (sim_fpu)
+ufpu2fpu (const sim_ufpu *d)
+{
+ sim_fpu ans;
+ ans.val.i = pack_fpu (d, 1);
+ return ans;
+}
+
+
+STATIC_INLINE_SIM_FPU (sim_ufpu)
+fpu2ufpu (const sim_fpu *d)
+{
+ sim_ufpu ans;
+ unpack_fpu (&ans, d->val.i, 1);
+ return ans;
+}
+
+STATIC_INLINE_SIM_FPU (int)
+is_ufpu_number (const sim_ufpu *d)
+{
+ switch (d->class)
+ {
+ case sim_fpu_class_zero:
+ case sim_fpu_class_number:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+STATIC_INLINE_SIM_FPU (int)
+is_ufpu_nan (const sim_ufpu *d)
+{
+ switch (d->class)
+ {
+ case sim_fpu_class_qnan:
+ case sim_fpu_class_snan:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+STATIC_INLINE_SIM_FPU (int)
+is_ufpu_zero (const sim_ufpu *d)
+{
+ switch (d->class)
+ {
+ case sim_fpu_class_zero:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+STATIC_INLINE_SIM_FPU (int)
+is_ufpu_inf (const sim_ufpu *d)
+{
+ switch (d->class)
+ {
+ case sim_fpu_class_infinity:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+STATIC_INLINE_SIM_FPU (sim_fpu)
+fpu_nan (void)
+{
+ sim_ufpu tmp;
+ tmp.class = sim_fpu_class_snan;
+ tmp.fraction = 0;
+ tmp.sign = 1;
+ tmp.normal_exp = 0;
+ return ufpu2fpu (&tmp);
+}
+
+
+STATIC_INLINE_SIM_FPU (signed64)
+fpu2i (sim_fpu s, int is_double)
+{
+ sim_ufpu a = fpu2ufpu (&s);
+ unsigned64 tmp;
+ if (is_ufpu_zero (&a))
+ return 0;
+ if (is_ufpu_nan (&a))
+ return 0;
+ /* get reasonable MAX_SI_INT... */
+ if (is_ufpu_inf (&a))
+ return a.sign ? MAX_SI_INT : (-MAX_SI_INT)-1;
+ /* it is a number, but a small one */
+ if (a.normal_exp < 0)
+ return 0;
+ if (a.normal_exp > (FRAC_NBITS - 2))
+ return a.sign ? (-MAX_SI_INT)-1 : MAX_SI_INT;
+ if (a.normal_exp > (FRACBITS + NGARDS + F_D_BITOFF))
+ tmp = (a.fraction << (a.normal_exp - (FRACBITS + NGARDS)));
+ else
+ tmp = (a.fraction >> ((FRACBITS + NGARDS + F_D_BITOFF) - a.normal_exp));
+ return a.sign ? (-tmp) : (tmp);
+}
+
+STATIC_INLINE_SIM_FPU (unsigned64)
+fpu2u (sim_fpu s, int is_double)
+{
+ sim_ufpu a = fpu2ufpu (&s);
+ unsigned64 tmp;
+ if (is_ufpu_zero (&a))
+ return 0;
+ if (is_ufpu_nan (&a))
+ return 0;
+ /* get reasonable MAX_USI_INT... */
+ if (is_ufpu_inf (&a))
+ return a.sign ? MAX_USI_INT : 0;
+ /* it is a negative number */
+ if (a.sign)
+ return 0;
+ /* it is a number, but a small one */
+ if (a.normal_exp < 0)
+ return 0;
+ if (a.normal_exp > (FRAC_NBITS - 1))
+ return MAX_USI_INT;
+ if (a.normal_exp > (FRACBITS + NGARDS + F_D_BITOFF))
+ tmp = (a.fraction << (a.normal_exp - (FRACBITS + NGARDS + F_D_BITOFF)));
+ else
+ tmp = (a.fraction >> ((FRACBITS + NGARDS + F_D_BITOFF) - a.normal_exp));
+ return tmp;
+}
+
+
/* register <-> sim_fpu */
INLINE_SIM_FPU (sim_fpu)
sim_fpu_32to (unsigned32 s)
{
- sim_fpu ans;
- ans.val = *(float*) &s;
- return ans;
+ sim_ufpu tmp;
+ unpack_fpu (&tmp, s, 0);
+ return ufpu2fpu (&tmp);
}
@@ -44,7 +433,7 @@ INLINE_SIM_FPU (sim_fpu)
sim_fpu_64to (unsigned64 s)
{
sim_fpu ans;
- ans.val = *(double*) &s;
+ ans.val.i = s;
return ans;
}
@@ -52,15 +441,16 @@ sim_fpu_64to (unsigned64 s)
INLINE_SIM_FPU (unsigned32)
sim_fpu_to32 (sim_fpu l)
{
- float s = l.val;
- return *(unsigned32*) &s;
+ /* convert to single safely */
+ sim_ufpu tmp = fpu2ufpu (&l);
+ return pack_fpu (&tmp, 0);
}
INLINE_SIM_FPU (unsigned64)
sim_fpu_to64 (sim_fpu s)
{
- return *(unsigned64*) &s.val;
+ return s.val.i;
}
@@ -71,7 +461,7 @@ sim_fpu_add (sim_fpu l,
sim_fpu r)
{
sim_fpu ans;
- ans.val = l.val + r.val;
+ ans.val.d = l.val.d + r.val.d;
return ans;
}
@@ -81,7 +471,7 @@ sim_fpu_sub (sim_fpu l,
sim_fpu r)
{
sim_fpu ans;
- ans.val = l.val - r.val;
+ ans.val.d = l.val.d - r.val.d;
return ans;
}
@@ -91,7 +481,7 @@ sim_fpu_mul (sim_fpu l,
sim_fpu r)
{
sim_fpu ans;
- ans.val = l.val * r.val;
+ ans.val.d = l.val.d * r.val.d;
return ans;
}
@@ -100,9 +490,90 @@ INLINE_SIM_FPU (sim_fpu)
sim_fpu_div (sim_fpu l,
sim_fpu r)
{
- sim_fpu ans;
- ans.val = l.val / r.val;
- return ans;
+ const int is_double = 1;
+ sim_ufpu a = fpu2ufpu (&l);
+ sim_ufpu b = fpu2ufpu (&r);
+ unsigned64 bit;
+ unsigned64 numerator;
+ unsigned64 denominator;
+ unsigned64 quotient;
+
+ if (is_ufpu_nan (&a))
+ {
+ return ufpu2fpu (&a);
+ }
+ if (is_ufpu_nan (&b))
+ {
+ return ufpu2fpu (&b);
+ }
+ if (is_ufpu_inf (&a) || is_ufpu_zero (&a))
+ {
+ if (a.class == b.class)
+ return fpu_nan ();
+ return l;
+ }
+ a.sign = a.sign ^ b.sign;
+
+ if (is_ufpu_inf (&b))
+ {
+ a.fraction = 0;
+ a.normal_exp = 0;
+ return ufpu2fpu (&a);
+ }
+ if (is_ufpu_zero (&b))
+ {
+ a.class = sim_fpu_class_infinity;
+ return ufpu2fpu (&a);
+ }
+
+ /* Calculate the mantissa by multiplying both 64bit numbers to get a
+ 128 bit number */
+ {
+ /* quotient =
+ ( numerator / denominator) * 2^(numerator exponent - denominator exponent)
+ */
+
+ a.normal_exp = a.normal_exp - b.normal_exp;
+ numerator = a.fraction;
+ denominator = b.fraction;
+
+ if (numerator < denominator)
+ {
+ /* Fraction will be less than 1.0 */
+ numerator *= 2;
+ a.normal_exp--;
+ }
+ bit = IMPLICIT_1;
+ quotient = 0;
+ /* ??? Does divide one bit at a time. Optimize. */
+ while (bit)
+ {
+ if (numerator >= denominator)
+ {
+ quotient |= bit;
+ numerator -= denominator;
+ }
+ bit >>= 1;
+ numerator *= 2;
+ }
+
+ if ((quotient & GARDMASK) == GARDMSB)
+ {
+ if (quotient & (1 << NGARDS))
+ {
+ /* half way, so round to even */
+ quotient += GARDROUND + 1;
+ }
+ else if (numerator)
+ {
+ /* but we really weren't half way, more bits exist */
+ quotient += GARDROUND + 1;
+ }
+ }
+
+ a.fraction = quotient;
+ return ufpu2fpu (&a);
+ }
}
@@ -110,7 +581,7 @@ INLINE_SIM_FPU (sim_fpu)
sim_fpu_inv (sim_fpu r)
{
sim_fpu ans;
- ans.val = 1 / r.val;
+ ans.val.d = 1 / r.val.d;
return ans;
}
@@ -119,7 +590,7 @@ INLINE_SIM_FPU (sim_fpu)
sim_fpu_sqrt (sim_fpu r)
{
sim_fpu ans;
- ans.val = sqrt (r.val);
+ ans.val.d = sqrt (r.val.d);
return ans;
}
@@ -130,75 +601,98 @@ INLINE_SIM_FPU (sim_fpu)
sim_fpu_i32to (signed32 s)
{
sim_fpu ans;
- ans.val = s;
+ ans.val.d = s;
return ans;
}
+INLINE_SIM_FPU (signed32)
+sim_fpu_to32i (sim_fpu s)
+{
+ return fpu2i (s, 0);
+}
+
+
INLINE_SIM_FPU (sim_fpu)
sim_fpu_u32to (unsigned32 s)
{
sim_fpu ans;
- ans.val = s;
+ ans.val.d = s;
return ans;
}
+INLINE_SIM_FPU (unsigned32)
+sim_fpu_to32u (sim_fpu s)
+{
+ return fpu2u (s, 0);
+}
+
+
INLINE_SIM_FPU (sim_fpu)
sim_fpu_i64to (signed64 s)
{
sim_fpu ans;
- ans.val = s;
+ ans.val.d = s;
return ans;
}
+INLINE_SIM_FPU (signed64)
+sim_fpu_to64i (sim_fpu s)
+{
+ return fpu2i (s, 1);
+}
+
+
INLINE_SIM_FPU (sim_fpu)
sim_fpu_u64to (unsigned64 s)
{
sim_fpu ans;
- ans.val = s;
+ ans.val.d = s;
return ans;
}
+INLINE_SIM_FPU (unsigned64)
+sim_fpu_to64u (sim_fpu s)
+{
+ return fpu2u (s, 1);
+}
+
+
/* sim_fpu -> host format */
INLINE_SIM_FPU (float)
sim_fpu_2f (sim_fpu f)
{
- return f.val;
+ return f.val.d;
}
INLINE_SIM_FPU (double)
sim_fpu_2d (sim_fpu s)
{
- return s.val;
+ return s.val.d;
}
-#if 0
INLINE_SIM_FPU (sim_fpu)
sim_fpu_f2 (float f)
{
sim_fpu ans;
- ans.val = f;
+ ans.val.d = f;
return ans;
}
-#endif
-#if 0
INLINE_SIM_FPU (sim_fpu)
sim_fpu_d2 (double d)
{
sim_fpu ans;
- ans.val = d;
+ ans.val.d = d;
return ans;
}
-#endif
-
/* General */
@@ -206,7 +700,8 @@ sim_fpu_d2 (double d)
INLINE_SIM_FPU (int)
sim_fpu_is_nan (sim_fpu d)
{
- return 0; /* FIXME - detect NaN */
+ sim_ufpu tmp = fpu2ufpu (&d);
+ return is_ufpu_nan (&tmp);
}
@@ -216,42 +711,72 @@ INLINE_SIM_FPU (int)
sim_fpu_is_lt (sim_fpu l,
sim_fpu r)
{
- return (l.val < r.val);
+ sim_ufpu tl = fpu2ufpu (&l);
+ sim_ufpu tr = fpu2ufpu (&r);
+ if (is_ufpu_number (&tl) && is_ufpu_number (&tr))
+ return (l.val.d < r.val.d);
+ else
+ return 0;
}
INLINE_SIM_FPU (int)
sim_fpu_is_le (sim_fpu l,
sim_fpu r)
{
- return (l.val <= r.val);
+ sim_ufpu tl = fpu2ufpu (&l);
+ sim_ufpu tr = fpu2ufpu (&r);
+ if (is_ufpu_number (&tl) && is_ufpu_number (&tr))
+ return (l.val.d <= r.val.d);
+ else
+ return 0;
}
INLINE_SIM_FPU (int)
sim_fpu_is_eq (sim_fpu l,
sim_fpu r)
{
- return (l.val == r.val);
+ sim_ufpu tl = fpu2ufpu (&l);
+ sim_ufpu tr = fpu2ufpu (&r);
+ if (is_ufpu_number (&tl) && is_ufpu_number (&tr))
+ return (l.val.d == r.val.d);
+ else
+ return 0;
}
INLINE_SIM_FPU (int)
sim_fpu_is_ne (sim_fpu l,
sim_fpu r)
{
- return (l.val != r.val);
+ sim_ufpu tl = fpu2ufpu (&l);
+ sim_ufpu tr = fpu2ufpu (&r);
+ if (is_ufpu_number (&tl) && is_ufpu_number (&tr))
+ return (l.val.d != r.val.d);
+ else
+ return 0;
}
INLINE_SIM_FPU (int)
sim_fpu_is_ge (sim_fpu l,
sim_fpu r)
{
- return (l.val >= r.val);
+ sim_ufpu tl = fpu2ufpu (&l);
+ sim_ufpu tr = fpu2ufpu (&r);
+ if (is_ufpu_number (&tl) && is_ufpu_number (&tr))
+ return (l.val.d >= r.val.d);
+ else
+ return 0;
}
INLINE_SIM_FPU (int)
sim_fpu_is_gt (sim_fpu l,
sim_fpu r)
{
- return (l.val > r.val);
+ sim_ufpu tl = fpu2ufpu (&l);
+ sim_ufpu tr = fpu2ufpu (&r);
+ if (is_ufpu_number (&tl) && is_ufpu_number (&tr))
+ return (l.val.d > r.val.d);
+ else
+ return 0;
}
#endif