of the floating point routines in libgcc1.c for targets without
hardware floating point. */
-/* Copyright (C) 1994,1997 Free Software Foundation, Inc.
-
-This file is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
-later version.
-
-In addition to the permissions in the GNU General Public License, the
-Free Software Foundation gives you unlimited permission to link the
-compiled version of this file with other programs, and to distribute
-those programs without any restriction coming from the use of this
-file. (The General Public License restrictions do apply in other
-respects; for example, they cover modification of the file, and
-distribution when not linked into another program.)
-
-This file is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
+/* Copyright 1994, 1997, 1998, 2003, 2007, 2008, 2009, 2010
+Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with this program; see the file COPYING. If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, if you link this library with other files,
some of which are compiled with GCC, to produce an executable,
#include "sim-assert.h"
-/* Debugging support. */
+/* Debugging support.
+ If digits is -1, then print all digits. */
static void
print_bits (unsigned64 x,
int msbit,
+ int digits,
sim_fpu_print_func print,
void *arg)
{
unsigned64 bit = LSBIT64 (msbit);
int i = 4;
- while (bit)
+ while (bit && digits)
{
if (i == 0)
print (arg, ",");
+
if ((x & bit))
print (arg, "1");
else
print (arg, "0");
bit >>= 1;
+
+ if (digits > 0) digits--;
i = (i + 1) % 4;
}
}
*/
#define NR_EXPBITS (is_double ? 11 : 8)
-#define NR_FRACBITS (is_double ? 52 : 23)
+#define NR_FRACBITS (is_double ? 52 : 23)
#define SIGNBIT (is_double ? MSBIT64 (0) : MSBIT64 (32))
-#define EXPMAX ((unsigned) (is_double ? 2047 : 255))
-#define EXPBIAS (is_double ? 1023 : 127)
+#define EXPMAX32 (255)
+#define EXMPAX64 (2047)
+#define EXPMAX ((unsigned) (is_double ? EXMPAX64 : EXPMAX32))
+
+#define EXPBIAS32 (127)
+#define EXPBIAS64 (1023)
+#define EXPBIAS (is_double ? EXPBIAS64 : EXPBIAS32)
#define QUIET_NAN LSBIT64 (NR_FRACBITS - 1)
64 bit - <IMPLICIT_1:1><FRACBITS:52><GUARDS:8><PAD:00>
32 bit - <IMPLICIT_1:1><FRACBITS:23><GUARDS:7><PAD:30> */
-#define NR_PAD (is_double ? 0 : 30)
-#define PADMASK (is_double ? 0 : LSMASK64 (29, 0))
-#define NR_GUARDS ((is_double ? 8 : 7 ) + NR_PAD)
+#define NR_PAD32 (30)
+#define NR_PAD64 (0)
+#define NR_PAD (is_double ? NR_PAD64 : NR_PAD32)
+#define PADMASK (is_double ? 0 : LSMASK64 (NR_PAD32 - 1, 0))
+
+#define NR_GUARDS32 (7 + NR_PAD32)
+#define NR_GUARDS64 (8 + NR_PAD64)
+#define NR_GUARDS (is_double ? NR_GUARDS64 : NR_GUARDS32)
#define GUARDMASK LSMASK64 (NR_GUARDS - 1, 0)
+
#define GUARDMSB LSBIT64 (NR_GUARDS - 1)
#define GUARDLSB LSBIT64 (NR_PAD)
#define GUARDROUND LSMASK64 (NR_GUARDS - 2, 0)
#define FRAC32MASK LSMASK64 (63, NR_FRAC_GUARD - 32 + 1)
#define NORMAL_EXPMIN (-(EXPBIAS)+1)
+
+#define NORMAL_EXPMAX32 (EXPBIAS32)
+#define NORMAL_EXPMAX64 (EXPBIAS64)
#define NORMAL_EXPMAX (EXPBIAS)
#define MAX_UINT (is_64bit ? MAX_UINT64 : MAX_UINT32)
#define NR_INTBITS (is_64bit ? 64 : 32)
+/* Squeese an unpacked sim_fpu struct into a 32/64 bit integer */
STATIC_INLINE_SIM_FPU (unsigned64)
pack_fpu (const sim_fpu *src,
int is_double)
/* force fraction to correct class */
fraction = src->fraction;
fraction >>= NR_GUARDS;
+#ifdef SIM_QUIET_NAN_NEGATED
+ fraction |= QUIET_NAN - 1;
+#else
fraction |= QUIET_NAN;
+#endif
break;
case sim_fpu_class_snan:
sign = src->sign;
/* force fraction to correct class */
fraction = src->fraction;
fraction >>= NR_GUARDS;
+#ifdef SIM_QUIET_NAN_NEGATED
+ fraction |= QUIET_NAN;
+#else
fraction &= ~QUIET_NAN;
+#endif
break;
case sim_fpu_class_infinity:
sign = src->sign;
fraction = 0;
break;
case sim_fpu_class_number:
+ case sim_fpu_class_denorm:
ASSERT (src->fraction >= IMPLICIT_1);
ASSERT (src->fraction < IMPLICIT_2);
if (src->normal_exp < NORMAL_EXPMIN)
}
+/* Unpack a 32/64 bit integer into a sim_fpu structure */
STATIC_INLINE_SIM_FPU (void)
unpack_fpu (sim_fpu *dst, unsigned64 packed, int is_double)
{
/* tastes like zero */
dst->class = sim_fpu_class_zero;
dst->sign = sign;
+ dst->normal_exp = 0;
}
else
{
so there isn't a leading implicit one - we'll shift it so
it gets one. */
dst->normal_exp = exp - EXPBIAS + 1;
- dst->class = sim_fpu_class_number;
+ dst->class = sim_fpu_class_denorm;
dst->sign = sign;
fraction <<= NR_GUARDS;
while (fraction < IMPLICIT_1)
}
else
{
+ int qnan;
+
/* Non zero fraction, means NaN */
dst->sign = sign;
dst->fraction = (fraction << NR_GUARDS);
- if (fraction >= QUIET_NAN)
+#ifdef SIM_QUIET_NAN_NEGATED
+ qnan = (fraction & QUIET_NAN) == 0;
+#else
+ qnan = fraction >= QUIET_NAN;
+#endif
+ if (qnan)
dst->class = sim_fpu_class_qnan;
else
dst->class = sim_fpu_class_snan;
}
+/* Convert a floating point into an integer */
STATIC_INLINE_SIM_FPU (int)
fpu2i (signed64 *i,
const sim_fpu *s,
return status;
}
+/* convert an integer into a floating point */
STATIC_INLINE_SIM_FPU (int)
i2fpu (sim_fpu *f, signed64 i, int is_64bit)
{
{
f->class = sim_fpu_class_zero;
f->sign = 0;
+ f->normal_exp = 0;
}
else
{
{
do
{
- f->fraction >>= 1;
+ f->fraction = (f->fraction >> 1) | (f->fraction & 1);
f->normal_exp += 1;
}
while (f->fraction >= IMPLICIT_2);
}
+/* Convert a floating point into an integer */
STATIC_INLINE_SIM_FPU (int)
fpu2u (unsigned64 *u, const sim_fpu *s, int is_64bit)
{
return 0;
}
+/* Convert an unsigned integer into a floating point */
STATIC_INLINE_SIM_FPU (int)
u2fpu (sim_fpu *f, unsigned64 u, int is_64bit)
{
{
f->class = sim_fpu_class_zero;
f->sign = 0;
+ f->normal_exp = 0;
}
else
{
}
+INLINE_SIM_FPU (void)
+sim_fpu_fractionto (sim_fpu *f,
+ int sign,
+ int normal_exp,
+ unsigned64 fraction,
+ int precision)
+{
+ int shift = (NR_FRAC_GUARD - precision);
+ f->class = sim_fpu_class_number;
+ f->sign = sign;
+ f->normal_exp = normal_exp;
+ /* shift the fraction to where sim-fpu expects it */
+ if (shift >= 0)
+ f->fraction = (fraction << shift);
+ else
+ f->fraction = (fraction >> -shift);
+ f->fraction |= IMPLICIT_1;
+}
+
+
+INLINE_SIM_FPU (unsigned64)
+sim_fpu_tofraction (const sim_fpu *d,
+ int precision)
+{
+ /* we have NR_FRAC_GUARD bits, we want only PRECISION bits */
+ int shift = (NR_FRAC_GUARD - precision);
+ unsigned64 fraction = (d->fraction & ~IMPLICIT_1);
+ if (shift >= 0)
+ return fraction >> shift;
+ else
+ return fraction << -shift;
+}
+
+
/* Rounding */
STATIC_INLINE_SIM_FPU (int)
return sim_fpu_status_invalid_snan;
break;
case sim_fpu_class_number:
+ case sim_fpu_class_denorm:
{
int status;
ASSERT (f->fraction < IMPLICIT_2);
if (shift + NR_GUARDS <= NR_FRAC_GUARD + 1
&& !(denorm & sim_fpu_denorm_zero))
{
-
status = do_normal_round (f, shift + NR_GUARDS, round);
if (f->fraction == 0) /* rounding underflowed */
- status |= do_normal_underflow (f, is_double, round);
+ {
+ status |= do_normal_underflow (f, is_double, round);
+ }
else if (f->normal_exp < NORMAL_EXPMIN) /* still underflow? */
{
status |= sim_fpu_status_denorm;
before rounding, some after! */
if (status & sim_fpu_status_inexact)
status |= sim_fpu_status_underflow;
+ /* Flag that resultant value has been denormalized */
+ f->class = sim_fpu_class_denorm;
}
else if ((denorm & sim_fpu_denorm_underflow_inexact))
{
/* oops! rounding caused overflow */
status |= do_normal_overflow (f, is_double, round);
}
- ASSERT ((f->class == sim_fpu_class_number)
+ ASSERT ((f->class == sim_fpu_class_number
+ || f->class == sim_fpu_class_denorm)
<= (f->fraction < IMPLICIT_2 && f->fraction >= IMPLICIT_1));
return status;
}
ASSERT (high >= LSBIT64 ((NR_FRAC_GUARD * 2) - 64));
ASSERT (LSBIT64 (((NR_FRAC_GUARD + 1) * 2) - 64) < IMPLICIT_1);
-#if 0
- printf ("\n");
- print_bits (high, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf (";");
- print_bits (low, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf ("\n");
-#endif
-
/* normalize */
do
{
}
while (high < IMPLICIT_1);
-#if 0
- print_bits (high, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf (";");
- print_bits (low, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf ("\n");
-#endif
-
ASSERT (high >= IMPLICIT_1 && high < IMPLICIT_2);
if (low != 0)
{
numerator <<= 1;
}
-#if 0
- printf ("\n");
- print_bits (quotient, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf ("\n");
- print_bits (numerator, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf ("\n");
- print_bits (denominator, 63, (sim_fpu_print_func*)fprintf, stdout);
- printf ("\n");
-#endif
-
/* discard (but save) the extra bits */
if ((quotient & LSMASK64 (NR_SPARE -1, 0)))
quotient = (quotient >> NR_SPARE) | 1;
INLINE_SIM_FPU (int)
-sim_fpu_neg (sim_fpu *f,
+sim_fpu_max (sim_fpu *f,
+ const sim_fpu *l,
const sim_fpu *r)
{
+ if (sim_fpu_is_snan (l))
+ {
+ *f = *l;
+ f->class = sim_fpu_class_qnan;
+ return sim_fpu_status_invalid_snan;
+ }
if (sim_fpu_is_snan (r))
{
*f = *r;
f->class = sim_fpu_class_qnan;
return sim_fpu_status_invalid_snan;
}
+ if (sim_fpu_is_qnan (l))
+ {
+ *f = *l;
+ return 0;
+ }
if (sim_fpu_is_qnan (r))
{
*f = *r;
return 0;
}
- *f = *r;
- f->sign = !r->sign;
- return 0;
+ if (sim_fpu_is_infinity (l))
+ {
+ if (sim_fpu_is_infinity (r)
+ && l->sign == r->sign)
+ {
+ *f = sim_fpu_qnan;
+ return sim_fpu_status_invalid_isi;
+ }
+ if (l->sign)
+ *f = *r; /* -inf < anything */
+ else
+ *f = *l; /* +inf > anthing */
+ return 0;
+ }
+ if (sim_fpu_is_infinity (r))
+ {
+ if (r->sign)
+ *f = *l; /* anything > -inf */
+ else
+ *f = *r; /* anthing < +inf */
+ return 0;
+ }
+ if (l->sign > r->sign)
+ {
+ *f = *r; /* -ve < +ve */
+ return 0;
+ }
+ if (l->sign < r->sign)
+ {
+ *f = *l; /* +ve > -ve */
+ return 0;
+ }
+ ASSERT (l->sign == r->sign);
+ if (l->normal_exp > r->normal_exp
+ || (l->normal_exp == r->normal_exp &&
+ l->fraction > r->fraction))
+ {
+ /* |l| > |r| */
+ if (l->sign)
+ *f = *r; /* -ve < -ve */
+ else
+ *f = *l; /* +ve > +ve */
+ return 0;
+ }
+ else
+ {
+ /* |l| <= |r| */
+ if (l->sign)
+ *f = *l; /* -ve > -ve */
+ else
+ *f = *r; /* +ve < +ve */
+ return 0;
+ }
}
INLINE_SIM_FPU (int)
-sim_fpu_abs (sim_fpu *f,
+sim_fpu_min (sim_fpu *f,
+ const sim_fpu *l,
const sim_fpu *r)
{
+ if (sim_fpu_is_snan (l))
+ {
+ *f = *l;
+ f->class = sim_fpu_class_qnan;
+ return sim_fpu_status_invalid_snan;
+ }
if (sim_fpu_is_snan (r))
{
*f = *r;
f->class = sim_fpu_class_qnan;
return sim_fpu_status_invalid_snan;
}
+ if (sim_fpu_is_qnan (l))
+ {
+ *f = *l;
+ return 0;
+ }
if (sim_fpu_is_qnan (r))
{
*f = *r;
return 0;
}
- *f = *r;
- f->sign = 0;
- return 0;
+ if (sim_fpu_is_infinity (l))
+ {
+ if (sim_fpu_is_infinity (r)
+ && l->sign == r->sign)
+ {
+ *f = sim_fpu_qnan;
+ return sim_fpu_status_invalid_isi;
+ }
+ if (l->sign)
+ *f = *l; /* -inf < anything */
+ else
+ *f = *r; /* +inf > anthing */
+ return 0;
+ }
+ if (sim_fpu_is_infinity (r))
+ {
+ if (r->sign)
+ *f = *r; /* anything > -inf */
+ else
+ *f = *l; /* anything < +inf */
+ return 0;
+ }
+ if (l->sign > r->sign)
+ {
+ *f = *l; /* -ve < +ve */
+ return 0;
+ }
+ if (l->sign < r->sign)
+ {
+ *f = *r; /* +ve > -ve */
+ return 0;
+ }
+ ASSERT (l->sign == r->sign);
+ if (l->normal_exp > r->normal_exp
+ || (l->normal_exp == r->normal_exp &&
+ l->fraction > r->fraction))
+ {
+ /* |l| > |r| */
+ if (l->sign)
+ *f = *l; /* -ve < -ve */
+ else
+ *f = *r; /* +ve > +ve */
+ return 0;
+ }
+ else
+ {
+ /* |l| <= |r| */
+ if (l->sign)
+ *f = *r; /* -ve > -ve */
+ else
+ *f = *l; /* +ve < +ve */
+ return 0;
+ }
}
INLINE_SIM_FPU (int)
-sim_fpu_inv (sim_fpu *f,
+sim_fpu_neg (sim_fpu *f,
const sim_fpu *r)
{
if (sim_fpu_is_snan (r))
if (sim_fpu_is_qnan (r))
{
*f = *r;
- f->class = sim_fpu_class_qnan;
return 0;
}
- if (sim_fpu_is_infinity (r))
- {
- *f = sim_fpu_zero;
- f->sign = r->sign;
- return 0;
- }
- if (sim_fpu_is_zero (r))
+ *f = *r;
+ f->sign = !r->sign;
+ return 0;
+}
+
+
+INLINE_SIM_FPU (int)
+sim_fpu_abs (sim_fpu *f,
+ const sim_fpu *r)
+{
+ *f = *r;
+ f->sign = 0;
+ if (sim_fpu_is_snan (r))
{
- f->class = sim_fpu_class_infinity;
- f->sign = r->sign;
- return sim_fpu_status_invalid_div0;
+ f->class = sim_fpu_class_qnan;
+ return sim_fpu_status_invalid_snan;
}
- *f = *r;
- f->normal_exp = - r->normal_exp;
return 0;
}
+INLINE_SIM_FPU (int)
+sim_fpu_inv (sim_fpu *f,
+ const sim_fpu *r)
+{
+ return sim_fpu_div (f, &sim_fpu_one, r);
+}
+
+
INLINE_SIM_FPU (int)
sim_fpu_sqrt (sim_fpu *f,
const sim_fpu *r)
{
f->class = sim_fpu_class_zero;
f->sign = r->sign;
+ f->normal_exp = 0;
return 0;
}
if (sim_fpu_is_infinity (r))
sim_fpu_2d (const sim_fpu *s)
{
sim_fpu_map val;
- val.i = pack_fpu (s, 1);
+ if (sim_fpu_is_snan (s))
+ {
+ /* gag SNaN's */
+ sim_fpu n = *s;
+ n.class = sim_fpu_class_qnan;
+ val.i = pack_fpu (&n, 1);
+ }
+ else
+ {
+ val.i = pack_fpu (s, 1);
+ }
return val.d;
}
{
switch (d->class)
{
+ case sim_fpu_class_denorm:
case sim_fpu_class_number:
return 1;
default:
}
}
+INLINE_SIM_FPU (int)
+sim_fpu_is_denorm (const sim_fpu *d)
+{
+ switch (d->class)
+ {
+ case sim_fpu_class_denorm:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
+INLINE_SIM_FPU (int)
+sim_fpu_sign (const sim_fpu *d)
+{
+ return d->sign;
+}
+
+
+INLINE_SIM_FPU (int)
+sim_fpu_exp (const sim_fpu *d)
+{
+ return d->normal_exp;
+}
+
+
+INLINE_SIM_FPU (unsigned64)
+sim_fpu_fraction (const sim_fpu *d)
+{
+ return d->fraction;
+}
+
+
+INLINE_SIM_FPU (unsigned64)
+sim_fpu_guard (const sim_fpu *d, int is_double)
+{
+ unsigned64 rv;
+ unsigned64 guardmask = LSMASK64 (NR_GUARDS - 1, 0);
+ rv = (d->fraction & guardmask) >> NR_PAD;
+ return rv;
+}
+
+
INLINE_SIM_FPU (int)
sim_fpu_is (const sim_fpu *d)
{
case sim_fpu_class_snan:
return SIM_FPU_IS_SNAN;
case sim_fpu_class_infinity:
- return SIM_FPU_IS_NINF;
- return SIM_FPU_IS_PINF;
+ if (d->sign)
+ return SIM_FPU_IS_NINF;
+ else
+ return SIM_FPU_IS_PINF;
case sim_fpu_class_number:
if (d->sign)
- return SIM_FPU_IS_NNUM;
+ return SIM_FPU_IS_NNUMBER;
else
- return SIM_FPU_IS_PNUM;
-#if 0
- /* FIXME: Since the intermediate sim_fpu format can hold numbers
- far smaller then the targets FP format, the test for denorm
- is currently bogus. Perhaphs the code converting a number to
- the internal format should flag such situtations with
- `ndemorm' */
- case ???:
+ return SIM_FPU_IS_PNUMBER;
+ case sim_fpu_class_denorm:
if (d->sign)
return SIM_FPU_IS_NDENORM;
else
return SIM_FPU_IS_PDENORM;
-#endif
case sim_fpu_class_zero:
if (d->sign)
return SIM_FPU_IS_NZERO;
/* A number of useful constants */
-const sim_fpu sim_fpu_zero = { sim_fpu_class_zero, };
-const sim_fpu sim_fpu_qnan = { sim_fpu_class_qnan, };
+#if EXTERN_SIM_FPU_P
+const sim_fpu sim_fpu_zero = {
+ sim_fpu_class_zero, 0, 0, 0
+};
+const sim_fpu sim_fpu_qnan = {
+ sim_fpu_class_qnan, 0, 0, 0
+};
+const sim_fpu sim_fpu_one = {
+ sim_fpu_class_number, 0, IMPLICIT_1, 0
+};
+const sim_fpu sim_fpu_two = {
+ sim_fpu_class_number, 0, IMPLICIT_1, 1
+};
+const sim_fpu sim_fpu_max32 = {
+ sim_fpu_class_number, 0, LSMASK64 (NR_FRAC_GUARD, NR_GUARDS32), NORMAL_EXPMAX32
+};
+const sim_fpu sim_fpu_max64 = {
+ sim_fpu_class_number, 0, LSMASK64 (NR_FRAC_GUARD, NR_GUARDS64), NORMAL_EXPMAX64
+};
+#endif
/* For debugging */
sim_fpu_print_fpu (const sim_fpu *f,
sim_fpu_print_func *print,
void *arg)
+{
+ sim_fpu_printn_fpu (f, print, -1, arg);
+}
+
+INLINE_SIM_FPU (void)
+sim_fpu_printn_fpu (const sim_fpu *f,
+ sim_fpu_print_func *print,
+ int digits,
+ void *arg)
{
print (arg, "%s", f->sign ? "-" : "+");
switch (f->class)
{
case sim_fpu_class_qnan:
print (arg, "0.");
- print_bits (f->fraction, NR_FRAC_GUARD - 1, print, arg);
+ print_bits (f->fraction, NR_FRAC_GUARD - 1, digits, print, arg);
print (arg, "*QuietNaN");
break;
case sim_fpu_class_snan:
print (arg, "0.");
- print_bits (f->fraction, NR_FRAC_GUARD - 1, print, arg);
+ print_bits (f->fraction, NR_FRAC_GUARD - 1, digits, print, arg);
print (arg, "*SignalNaN");
break;
case sim_fpu_class_zero:
print (arg, "INF");
break;
case sim_fpu_class_number:
+ case sim_fpu_class_denorm:
print (arg, "1.");
- print_bits (f->fraction, NR_FRAC_GUARD - 1, print, arg);
- print (arg, "*2^%+-5d", f->normal_exp);
+ print_bits (f->fraction, NR_FRAC_GUARD - 1, digits, print, arg);
+ print (arg, "*2^%+d", f->normal_exp);
ASSERT (f->fraction >= IMPLICIT_1);
ASSERT (f->fraction < IMPLICIT_2);
}
void *arg)
{
int i = 1;
- char *prefix = "";
+ const char *prefix = "";
while (status >= i)
{
switch ((sim_fpu_status) (status & i))
case sim_fpu_status_invalid_sqrt:
print (arg, "%sSQRT", prefix);
break;
- break;
case sim_fpu_status_inexact:
print (arg, "%sX", prefix);
break;
- break;
case sim_fpu_status_overflow:
print (arg, "%sO", prefix);
break;
- break;
case sim_fpu_status_underflow:
print (arg, "%sU", prefix);
break;
- break;
case sim_fpu_status_invalid_div0:
print (arg, "%s/", prefix);
break;
- break;
case sim_fpu_status_rounded:
print (arg, "%sR", prefix);
break;
- break;
}
i <<= 1;
prefix = ",";