X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=sim%2Fcommon%2Fsim-fpu.c;h=74f5fd488c3409c5546faffe28b2d94bac4aa460;hb=611fa2f46c4ea0ca2f4153b6cf7ec31b8486eb28;hp=66fe5d160ff37d4cd2025653ebdc850c329aaf58;hpb=d24f06eef2f3c0816fdae9398f13c0b471efeb18;p=deliverable%2Fbinutils-gdb.git diff --git a/sim/common/sim-fpu.c b/sim/common/sim-fpu.c index 66fe5d160f..74f5fd488c 100644 --- a/sim/common/sim-fpu.c +++ b/sim/common/sim-fpu.c @@ -1,257 +1,2646 @@ -/* Simulator Floating-point support. - Copyright (C) 1997 Free Software Foundation, Inc. - Contributed by Cygnus Support. +/* This is a software floating point library which can be used instead + of the floating point routines in libgcc1.c for targets without + hardware floating point. */ -This file is part of GDB, the GNU debugger. +/* Copyright 1994-2019 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 2, or (at your option) -any later version. +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; if not, write to the Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +You should have received a copy of the GNU General Public License +along with this program. If not, see . */ +/* As a special exception, if you link this library with other files, + some of which are compiled with GCC, to produce an executable, + this library does not by itself cause the resulting executable + to be covered by the GNU General Public License. + This exception does not however invalidate any other reasons why + the executable file might be covered by the GNU General Public License. */ +/* This implements IEEE 754 format arithmetic, but does not provide a + mechanism for setting the rounding mode, or for generating or handling + exceptions. -#ifndef _SIM_FPU_C_ -#define _SIM_FPU_C_ + The original code by Steve Chamberlain, hacked by Mark Eichin and Jim + Wilson, all of Cygnus Support. */ -#include "sim-main.h" + +#ifndef SIM_FPU_C +#define SIM_FPU_C + +#include "sim-basics.h" #include "sim-fpu.h" -#include +#include "sim-io.h" +#include "sim-assert.h" + +#ifdef HAVE_STDLIB_H +#include +#endif + +/* 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 && 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; + } +} + + + +/* Quick and dirty conversion between a host double and host 64bit int. */ + +typedef union +{ + double d; + unsigned64 i; +} sim_fpu_map; + + +/* A packed IEEE floating point number. + + Form is for both + 32 and 64 bit numbers. This number is interpreted as: + + Normalized (0 < BIASEDEXP && BIASEDEXP < EXPMAX): + (sign ? '-' : '+') 1. x 2 ^ (BIASEDEXP - EXPBIAS) + + Denormalized (0 == BIASEDEXP && FRAC != 0): + (sign ? "-" : "+") 0. x 2 ^ (- EXPBIAS) + + Zero (0 == BIASEDEXP && FRAC == 0): + (sign ? "-" : "+") 0.0 + + Infinity (BIASEDEXP == EXPMAX && FRAC == 0): + (sign ? "-" : "+") "infinity" + + SignalingNaN (BIASEDEXP == EXPMAX && FRAC > 0 && FRAC < QUIET_NAN): + SNaN.FRAC + + QuietNaN (BIASEDEXP == EXPMAX && FRAC > 0 && FRAC > QUIET_NAN): + QNaN.FRAC + + */ + +#define NR_EXPBITS (is_double ? 11 : 8) +#define NR_FRACBITS (is_double ? 52 : 23) +#define SIGNBIT (is_double ? MSBIT64 (0) : MSBIT64 (32)) + +#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) + + + +/* An unpacked floating point number. + + When unpacked, the fraction of both a 32 and 64 bit floating point + number is stored using the same format: + + 64 bit - + 32 bit - */ + +#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 NR_FRAC_GUARD (60) +#define IMPLICIT_1 LSBIT64 (NR_FRAC_GUARD) +#define IMPLICIT_2 LSBIT64 (NR_FRAC_GUARD + 1) +#define IMPLICIT_4 LSBIT64 (NR_FRAC_GUARD + 2) +#define NR_SPARE 2 + +#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) + + +/* Integer constants */ + +#define MAX_INT32 ((signed64) LSMASK64 (30, 0)) +#define MAX_UINT32 LSMASK64 (31, 0) +#define MIN_INT32 ((signed64) LSMASK64 (63, 31)) + +#define MAX_INT64 ((signed64) LSMASK64 (62, 0)) +#define MAX_UINT64 LSMASK64 (63, 0) +#define MIN_INT64 ((signed64) LSMASK64 (63, 63)) + +#define MAX_INT (is_64bit ? MAX_INT64 : MAX_INT32) +#define MIN_INT (is_64bit ? MIN_INT64 : MIN_INT32) +#define MAX_UINT (is_64bit ? MAX_UINT64 : MAX_UINT32) +#define NR_INTBITS (is_64bit ? 64 : 32) + +/* Squeeze 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) +{ + int sign; + unsigned64 exp; + unsigned64 fraction; + unsigned64 packed; + + switch (src->class) + { + /* Create a NaN. */ + case sim_fpu_class_qnan: + sign = src->sign; + exp = EXPMAX; + /* 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; + exp = EXPMAX; + /* 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; + exp = EXPMAX; + fraction = 0; + break; + case sim_fpu_class_zero: + sign = src->sign; + exp = 0; + 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) + { + /* This number's exponent is too low to fit into the bits + available in the number We'll denormalize the number by + storing zero in the exponent and shift the fraction to + the right to make up for it. */ + int nr_shift = NORMAL_EXPMIN - src->normal_exp; + if (nr_shift > NR_FRACBITS) + { + /* Underflow, just make the number zero. */ + sign = src->sign; + exp = 0; + fraction = 0; + } + else + { + sign = src->sign; + exp = 0; + /* Shift by the value. */ + fraction = src->fraction; + fraction >>= NR_GUARDS; + fraction >>= nr_shift; + } + } + else if (src->normal_exp > NORMAL_EXPMAX) + { + /* Infinity */ + sign = src->sign; + exp = EXPMAX; + fraction = 0; + } + else + { + exp = (src->normal_exp + EXPBIAS); + sign = src->sign; + fraction = src->fraction; + /* FIXME: Need to round according to WITH_SIM_FPU_ROUNDING + or some such. */ + /* Round to nearest: If the guard 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 & GUARDMASK) == GUARDMSB) + { + if ((fraction & (GUARDMSB << 1))) + fraction += (GUARDMSB << 1); + } + else + { + /* Add a one to the guards to force round to nearest. */ + fraction += GUARDROUND; + } + if ((fraction & IMPLICIT_2)) /* Rounding resulted in carry. */ + { + exp += 1; + fraction >>= 1; + } + fraction >>= NR_GUARDS; + /* When exp == EXPMAX (overflow from carry) fraction must + have been made zero. */ + ASSERT ((exp == EXPMAX) <= ((fraction & ~IMPLICIT_1) == 0)); + } + break; + default: + abort (); + } + + packed = ((sign ? SIGNBIT : 0) + | (exp << NR_FRACBITS) + | LSMASKED64 (fraction, NR_FRACBITS - 1, 0)); + + /* Trace operation. */ +#if 0 + if (is_double) + { + } + else + { + printf ("pack_fpu: "); + printf ("-> %c%0lX.%06lX\n", + LSMASKED32 (packed, 31, 31) ? '8' : '0', + (long) LSEXTRACTED32 (packed, 30, 23), + (long) LSEXTRACTED32 (packed, 23 - 1, 0)); + } +#endif + + return packed; +} + + +/* 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) +{ + unsigned64 fraction = LSMASKED64 (packed, NR_FRACBITS - 1, 0); + unsigned exp = LSEXTRACTED64 (packed, NR_EXPBITS + NR_FRACBITS - 1, NR_FRACBITS); + int sign = (packed & SIGNBIT) != 0; + + if (exp == 0) + { + /* Hmm. Looks like 0 */ + if (fraction == 0) + { + /* Tastes like zero. */ + dst->class = sim_fpu_class_zero; + dst->sign = sign; + dst->normal_exp = 0; + } + 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; + dst->class = sim_fpu_class_denorm; + dst->sign = sign; + fraction <<= NR_GUARDS; + while (fraction < IMPLICIT_1) + { + fraction <<= 1; + dst->normal_exp--; + } + dst->fraction = fraction; + } + } + else if (exp == EXPMAX) + { + /* Huge exponent*/ + if (fraction == 0) + { + /* Attached to a zero fraction - means infinity. */ + dst->class = sim_fpu_class_infinity; + dst->sign = sign; + /* dst->normal_exp = EXPBIAS; */ + /* dst->fraction = 0; */ + } + else + { + int qnan; + + /* Non zero fraction, means NaN. */ + dst->sign = sign; + dst->fraction = (fraction << NR_GUARDS); +#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; + } + } + else + { + /* Nothing strange about this number. */ + dst->class = sim_fpu_class_number; + dst->sign = sign; + dst->fraction = ((fraction << NR_GUARDS) | IMPLICIT_1); + dst->normal_exp = exp - EXPBIAS; + } + + /* Trace operation. */ +#if 0 + if (is_double) + { + } + else + { + printf ("unpack_fpu: %c%02lX.%06lX ->\n", + LSMASKED32 (packed, 31, 31) ? '8' : '0', + (long) LSEXTRACTED32 (packed, 30, 23), + (long) LSEXTRACTED32 (packed, 23 - 1, 0)); + } +#endif + + /* sanity checks */ + { + sim_fpu_map val; + val.i = pack_fpu (dst, 1); + if (is_double) + { + ASSERT (val.i == packed); + } + else + { + unsigned32 val = pack_fpu (dst, 0); + unsigned32 org = packed; + ASSERT (val == org); + } + } +} + + +/* Convert a floating point into an integer. */ +STATIC_INLINE_SIM_FPU (int) +fpu2i (signed64 *i, + const sim_fpu *s, + int is_64bit, + sim_fpu_round round) +{ + unsigned64 tmp; + int shift; + int status = 0; + if (sim_fpu_is_zero (s)) + { + *i = 0; + return 0; + } + if (sim_fpu_is_snan (s)) + { + *i = MIN_INT; /* FIXME */ + return sim_fpu_status_invalid_cvi; + } + if (sim_fpu_is_qnan (s)) + { + *i = MIN_INT; /* FIXME */ + return sim_fpu_status_invalid_cvi; + } + /* Map infinity onto MAX_INT... */ + if (sim_fpu_is_infinity (s)) + { + *i = s->sign ? MIN_INT : MAX_INT; + return sim_fpu_status_invalid_cvi; + } + /* It is a number, but a small one. */ + if (s->normal_exp < 0) + { + *i = 0; + return sim_fpu_status_inexact; + } + /* Is the floating point MIN_INT or just close? */ + if (s->sign && s->normal_exp == (NR_INTBITS - 1)) + { + *i = MIN_INT; + ASSERT (s->fraction >= IMPLICIT_1); + if (s->fraction == IMPLICIT_1) + return 0; /* exact */ + if (is_64bit) /* can't round */ + return sim_fpu_status_invalid_cvi; /* must be overflow */ + /* For a 32bit with MAX_INT, rounding is possible. */ + switch (round) + { + case sim_fpu_round_default: + abort (); + case sim_fpu_round_zero: + if ((s->fraction & FRAC32MASK) != IMPLICIT_1) + return sim_fpu_status_invalid_cvi; + else + return sim_fpu_status_inexact; + break; + case sim_fpu_round_near: + { + if ((s->fraction & FRAC32MASK) != IMPLICIT_1) + return sim_fpu_status_invalid_cvi; + else if ((s->fraction & !FRAC32MASK) >= (~FRAC32MASK >> 1)) + return sim_fpu_status_invalid_cvi; + else + return sim_fpu_status_inexact; + } + case sim_fpu_round_up: + if ((s->fraction & FRAC32MASK) == IMPLICIT_1) + return sim_fpu_status_inexact; + else + return sim_fpu_status_invalid_cvi; + case sim_fpu_round_down: + return sim_fpu_status_invalid_cvi; + } + } + /* Would right shifting result in the FRAC being shifted into + (through) the integer's sign bit? */ + if (s->normal_exp > (NR_INTBITS - 2)) + { + *i = s->sign ? MIN_INT : MAX_INT; + return sim_fpu_status_invalid_cvi; + } + /* Normal number, shift it into place. */ + tmp = s->fraction; + shift = (s->normal_exp - (NR_FRAC_GUARD)); + if (shift > 0) + { + tmp <<= shift; + } + else + { + shift = -shift; + if (tmp & ((SIGNED64 (1) << shift) - 1)) + status |= sim_fpu_status_inexact; + tmp >>= shift; + } + *i = s->sign ? (-tmp) : (tmp); + return status; +} + +/* Convert an integer into a floating point. */ +STATIC_INLINE_SIM_FPU (int) +i2fpu (sim_fpu *f, signed64 i, int is_64bit) +{ + int status = 0; + if (i == 0) + { + f->class = sim_fpu_class_zero; + f->sign = 0; + f->normal_exp = 0; + } + else + { + f->class = sim_fpu_class_number; + f->sign = (i < 0); + f->normal_exp = NR_FRAC_GUARD; + + if (f->sign) + { + /* Special case for minint, since there is no corresponding + +ve integer representation for it. */ + if (i == MIN_INT) + { + f->fraction = IMPLICIT_1; + f->normal_exp = NR_INTBITS - 1; + } + else + f->fraction = (-i); + } + else + f->fraction = i; + + if (f->fraction >= IMPLICIT_2) + { + do + { + f->fraction = (f->fraction >> 1) | (f->fraction & 1); + f->normal_exp += 1; + } + while (f->fraction >= IMPLICIT_2); + } + else if (f->fraction < IMPLICIT_1) + { + do + { + f->fraction <<= 1; + f->normal_exp -= 1; + } + while (f->fraction < IMPLICIT_1); + } + } + + /* trace operation */ +#if 0 + { + printf ("i2fpu: 0x%08lX ->\n", (long) i); + } +#endif + + /* sanity check */ + { + signed64 val; + fpu2i (&val, f, is_64bit, sim_fpu_round_zero); + if (i >= MIN_INT32 && i <= MAX_INT32) + { + ASSERT (val == i); + } + } + + return status; +} + + +/* Convert a floating point into an integer. */ +STATIC_INLINE_SIM_FPU (int) +fpu2u (unsigned64 *u, const sim_fpu *s, int is_64bit) +{ + const int is_double = 1; + unsigned64 tmp; + int shift; + if (sim_fpu_is_zero (s)) + { + *u = 0; + return 0; + } + if (sim_fpu_is_nan (s)) + { + *u = 0; + return 0; + } + /* It is a negative number. */ + if (s->sign) + { + *u = 0; + return 0; + } + /* Get reasonable MAX_USI_INT... */ + if (sim_fpu_is_infinity (s)) + { + *u = MAX_UINT; + return 0; + } + /* It is a number, but a small one. */ + if (s->normal_exp < 0) + { + *u = 0; + return 0; + } + /* overflow */ + if (s->normal_exp > (NR_INTBITS - 1)) + { + *u = MAX_UINT; + return 0; + } + /* normal number */ + tmp = (s->fraction & ~PADMASK); + shift = (s->normal_exp - (NR_FRACBITS + NR_GUARDS)); + if (shift > 0) + { + tmp <<= shift; + } + else + { + shift = -shift; + tmp >>= shift; + } + *u = tmp; + return 0; +} + +/* Convert an unsigned integer into a floating point. */ +STATIC_INLINE_SIM_FPU (int) +u2fpu (sim_fpu *f, unsigned64 u, int is_64bit) +{ + if (u == 0) + { + f->class = sim_fpu_class_zero; + f->sign = 0; + f->normal_exp = 0; + } + else + { + f->class = sim_fpu_class_number; + f->sign = 0; + f->normal_exp = NR_FRAC_GUARD; + f->fraction = u; + + while (f->fraction < IMPLICIT_1) + { + f->fraction <<= 1; + f->normal_exp -= 1; + } + } + return 0; +} /* register <-> sim_fpu */ -INLINE_SIM_FPU (sim_fpu) -sim_fpu_32to (unsigned32 s) +INLINE_SIM_FPU (void) +sim_fpu_32to (sim_fpu *f, unsigned32 s) +{ + unpack_fpu (f, s, 0); +} + + +INLINE_SIM_FPU (void) +sim_fpu_232to (sim_fpu *f, unsigned32 h, unsigned32 l) +{ + unsigned64 s = h; + s = (s << 32) | l; + unpack_fpu (f, s, 1); +} + + +INLINE_SIM_FPU (void) +sim_fpu_64to (sim_fpu *f, unsigned64 s) +{ + unpack_fpu (f, s, 1); +} + + +INLINE_SIM_FPU (void) +sim_fpu_to32 (unsigned32 *s, + const sim_fpu *f) +{ + *s = pack_fpu (f, 0); +} + + +INLINE_SIM_FPU (void) +sim_fpu_to232 (unsigned32 *h, unsigned32 *l, + const sim_fpu *f) { - sim_fpu ans; - ans.val = *(float*) &s; - return ans; + unsigned64 s = pack_fpu (f, 1); + *l = s; + *h = (s >> 32); } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_64to (unsigned64 s) +INLINE_SIM_FPU (void) +sim_fpu_to64 (unsigned64 *u, + const sim_fpu *f) { - sim_fpu ans; - ans.val = *(double*) &s; - return ans; + *u = pack_fpu (f, 1); } -INLINE_SIM_FPU (unsigned32) -sim_fpu_to32 (sim_fpu l) +INLINE_SIM_FPU (void) +sim_fpu_fractionto (sim_fpu *f, + int sign, + int normal_exp, + unsigned64 fraction, + int precision) { - float s = l.val; - return *(unsigned32*) &s; + 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_to64 (sim_fpu s) +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) +do_normal_overflow (sim_fpu *f, + int is_double, + sim_fpu_round round) +{ + switch (round) + { + case sim_fpu_round_default: + return 0; + case sim_fpu_round_near: + f->class = sim_fpu_class_infinity; + break; + case sim_fpu_round_up: + if (!f->sign) + f->class = sim_fpu_class_infinity; + break; + case sim_fpu_round_down: + if (f->sign) + f->class = sim_fpu_class_infinity; + break; + case sim_fpu_round_zero: + break; + } + f->normal_exp = NORMAL_EXPMAX; + f->fraction = LSMASK64 (NR_FRAC_GUARD, NR_GUARDS); + return (sim_fpu_status_overflow | sim_fpu_status_inexact); +} + +STATIC_INLINE_SIM_FPU (int) +do_normal_underflow (sim_fpu *f, + int is_double, + sim_fpu_round round) +{ + switch (round) + { + case sim_fpu_round_default: + return 0; + case sim_fpu_round_near: + f->class = sim_fpu_class_zero; + break; + case sim_fpu_round_up: + if (f->sign) + f->class = sim_fpu_class_zero; + break; + case sim_fpu_round_down: + if (!f->sign) + f->class = sim_fpu_class_zero; + break; + case sim_fpu_round_zero: + f->class = sim_fpu_class_zero; + break; + } + f->normal_exp = NORMAL_EXPMIN - NR_FRACBITS; + f->fraction = IMPLICIT_1; + return (sim_fpu_status_inexact | sim_fpu_status_underflow); +} + + + +/* Round a number using NR_GUARDS. + Will return the rounded number or F->FRACTION == 0 when underflow. */ + +STATIC_INLINE_SIM_FPU (int) +do_normal_round (sim_fpu *f, + int nr_guards, + sim_fpu_round round) { - return *(unsigned64*) &s.val; + unsigned64 guardmask = LSMASK64 (nr_guards - 1, 0); + unsigned64 guardmsb = LSBIT64 (nr_guards - 1); + unsigned64 fraclsb = guardmsb << 1; + if ((f->fraction & guardmask)) + { + int status = sim_fpu_status_inexact; + switch (round) + { + case sim_fpu_round_default: + return 0; + case sim_fpu_round_near: + if ((f->fraction & guardmsb)) + { + if ((f->fraction & fraclsb)) + { + status |= sim_fpu_status_rounded; + } + else if ((f->fraction & (guardmask >> 1))) + { + status |= sim_fpu_status_rounded; + } + } + break; + case sim_fpu_round_up: + if (!f->sign) + status |= sim_fpu_status_rounded; + break; + case sim_fpu_round_down: + if (f->sign) + status |= sim_fpu_status_rounded; + break; + case sim_fpu_round_zero: + break; + } + f->fraction &= ~guardmask; + /* Round if needed, handle resulting overflow. */ + if ((status & sim_fpu_status_rounded)) + { + f->fraction += fraclsb; + if ((f->fraction & IMPLICIT_2)) + { + f->fraction >>= 1; + f->normal_exp += 1; + } + } + return status; + } + else + return 0; } +STATIC_INLINE_SIM_FPU (int) +do_round (sim_fpu *f, + int is_double, + sim_fpu_round round, + sim_fpu_denorm denorm) +{ + switch (f->class) + { + case sim_fpu_class_qnan: + case sim_fpu_class_zero: + case sim_fpu_class_infinity: + return 0; + break; + case sim_fpu_class_snan: + /* Quieten a SignalingNaN. */ + f->class = sim_fpu_class_qnan; + return sim_fpu_status_invalid_snan; + break; + case sim_fpu_class_number: + case sim_fpu_class_denorm: + { + int status; + ASSERT (f->fraction < IMPLICIT_2); + ASSERT (f->fraction >= IMPLICIT_1); + if (f->normal_exp < NORMAL_EXPMIN) + { + /* This number's exponent is too low to fit into the bits + available in the number. Round off any bits that will be + discarded as a result of denormalization. Edge case is + the implicit bit shifted to GUARD0 and then rounded + up. */ + int shift = NORMAL_EXPMIN - f->normal_exp; + 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); + } + else if (f->normal_exp < NORMAL_EXPMIN) /* still underflow? */ + { + status |= sim_fpu_status_denorm; + /* Any loss of precision when denormalizing is + underflow. Some processors check for underflow + 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)) + { + if ((status & sim_fpu_status_inexact)) + status |= sim_fpu_status_underflow; + } + } + else + { + status = do_normal_underflow (f, is_double, round); + } + } + else if (f->normal_exp > NORMAL_EXPMAX) + { + /* Infinity */ + status = do_normal_overflow (f, is_double, round); + } + else + { + status = do_normal_round (f, NR_GUARDS, round); + if (f->fraction == 0) + /* f->class = sim_fpu_class_zero; */ + status |= do_normal_underflow (f, is_double, round); + else if (f->normal_exp > NORMAL_EXPMAX) + /* Oops! rounding caused overflow. */ + status |= do_normal_overflow (f, is_double, round); + } + ASSERT ((f->class == sim_fpu_class_number + || f->class == sim_fpu_class_denorm) + <= (f->fraction < IMPLICIT_2 && f->fraction >= IMPLICIT_1)); + return status; + } + } + return 0; +} + +INLINE_SIM_FPU (int) +sim_fpu_round_32 (sim_fpu *f, + sim_fpu_round round, + sim_fpu_denorm denorm) +{ + return do_round (f, 0, round, denorm); +} + +INLINE_SIM_FPU (int) +sim_fpu_round_64 (sim_fpu *f, + sim_fpu_round round, + sim_fpu_denorm denorm) +{ + return do_round (f, 1, round, denorm); +} + + + /* Arithmetic ops */ -INLINE_SIM_FPU (sim_fpu) -sim_fpu_add (sim_fpu l, - sim_fpu r) +INLINE_SIM_FPU (int) +sim_fpu_add (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; + } + 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; + } + *f = *l; + return 0; + } + if (sim_fpu_is_infinity (r)) + { + *f = *r; + return 0; + } + if (sim_fpu_is_zero (l)) + { + if (sim_fpu_is_zero (r)) + { + *f = sim_fpu_zero; + f->sign = l->sign & r->sign; + } + else + *f = *r; + return 0; + } + if (sim_fpu_is_zero (r)) + { + *f = *l; + return 0; + } + { + int status = 0; + int shift = l->normal_exp - r->normal_exp; + unsigned64 lfraction; + unsigned64 rfraction; + /* use exp of larger */ + if (shift >= NR_FRAC_GUARD) + { + /* left has much bigger magnitude */ + *f = *l; + return sim_fpu_status_inexact; + } + if (shift <= - NR_FRAC_GUARD) + { + /* right has much bigger magnitude */ + *f = *r; + return sim_fpu_status_inexact; + } + lfraction = l->fraction; + rfraction = r->fraction; + if (shift > 0) + { + f->normal_exp = l->normal_exp; + if (rfraction & LSMASK64 (shift - 1, 0)) + { + status |= sim_fpu_status_inexact; + rfraction |= LSBIT64 (shift); /* Stick LSBit. */ + } + rfraction >>= shift; + } + else if (shift < 0) + { + f->normal_exp = r->normal_exp; + if (lfraction & LSMASK64 (- shift - 1, 0)) + { + status |= sim_fpu_status_inexact; + lfraction |= LSBIT64 (- shift); /* Stick LSBit. */ + } + lfraction >>= -shift; + } + else + { + f->normal_exp = r->normal_exp; + } + + /* Perform the addition. */ + if (l->sign) + lfraction = - lfraction; + if (r->sign) + rfraction = - rfraction; + f->fraction = lfraction + rfraction; + + /* zero? */ + if (f->fraction == 0) + { + *f = sim_fpu_zero; + return 0; + } + + /* sign? */ + f->class = sim_fpu_class_number; + if (((signed64) f->fraction) >= 0) + f->sign = 0; + else + { + f->sign = 1; + f->fraction = - f->fraction; + } + + /* Normalize it. */ + if ((f->fraction & IMPLICIT_2)) + { + f->fraction = (f->fraction >> 1) | (f->fraction & 1); + f->normal_exp ++; + } + else if (f->fraction < IMPLICIT_1) + { + do + { + f->fraction <<= 1; + f->normal_exp --; + } + while (f->fraction < IMPLICIT_1); + } + ASSERT (f->fraction >= IMPLICIT_1 && f->fraction < IMPLICIT_2); + return status; + } +} + + +INLINE_SIM_FPU (int) +sim_fpu_sub (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; + } + 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; + } + *f = *l; + return 0; + } + if (sim_fpu_is_infinity (r)) + { + *f = *r; + f->sign = !r->sign; + return 0; + } + if (sim_fpu_is_zero (l)) + { + if (sim_fpu_is_zero (r)) + { + *f = sim_fpu_zero; + f->sign = l->sign & !r->sign; + } + else + { + *f = *r; + f->sign = !r->sign; + } + return 0; + } + if (sim_fpu_is_zero (r)) + { + *f = *l; + return 0; + } + { + int status = 0; + int shift = l->normal_exp - r->normal_exp; + unsigned64 lfraction; + unsigned64 rfraction; + /* use exp of larger */ + if (shift >= NR_FRAC_GUARD) + { + /* left has much bigger magnitude */ + *f = *l; + return sim_fpu_status_inexact; + } + if (shift <= - NR_FRAC_GUARD) + { + /* right has much bigger magnitude */ + *f = *r; + f->sign = !r->sign; + return sim_fpu_status_inexact; + } + lfraction = l->fraction; + rfraction = r->fraction; + if (shift > 0) + { + f->normal_exp = l->normal_exp; + if (rfraction & LSMASK64 (shift - 1, 0)) + { + status |= sim_fpu_status_inexact; + rfraction |= LSBIT64 (shift); /* Stick LSBit. */ + } + rfraction >>= shift; + } + else if (shift < 0) + { + f->normal_exp = r->normal_exp; + if (lfraction & LSMASK64 (- shift - 1, 0)) + { + status |= sim_fpu_status_inexact; + lfraction |= LSBIT64 (- shift); /* Stick LSBit. */ + } + lfraction >>= -shift; + } + else + { + f->normal_exp = r->normal_exp; + } + + /* Perform the subtraction. */ + if (l->sign) + lfraction = - lfraction; + if (!r->sign) + rfraction = - rfraction; + f->fraction = lfraction + rfraction; + + /* zero? */ + if (f->fraction == 0) + { + *f = sim_fpu_zero; + return 0; + } + + /* sign? */ + f->class = sim_fpu_class_number; + if (((signed64) f->fraction) >= 0) + f->sign = 0; + else + { + f->sign = 1; + f->fraction = - f->fraction; + } + + /* Normalize it. */ + if ((f->fraction & IMPLICIT_2)) + { + f->fraction = (f->fraction >> 1) | (f->fraction & 1); + f->normal_exp ++; + } + else if (f->fraction < IMPLICIT_1) + { + do + { + f->fraction <<= 1; + f->normal_exp --; + } + while (f->fraction < IMPLICIT_1); + } + ASSERT (f->fraction >= IMPLICIT_1 && f->fraction < IMPLICIT_2); + return status; + } +} + + +INLINE_SIM_FPU (int) +sim_fpu_mul (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; + } + if (sim_fpu_is_infinity (l)) + { + if (sim_fpu_is_zero (r)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_imz; + } + *f = *l; + f->sign = l->sign ^ r->sign; + return 0; + } + if (sim_fpu_is_infinity (r)) + { + if (sim_fpu_is_zero (l)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_imz; + } + *f = *r; + f->sign = l->sign ^ r->sign; + return 0; + } + if (sim_fpu_is_zero (l) || sim_fpu_is_zero (r)) + { + *f = sim_fpu_zero; + f->sign = l->sign ^ r->sign; + return 0; + } + /* Calculate the mantissa by multiplying both 64bit numbers to get a + 128 bit number. */ + { + unsigned64 low; + unsigned64 high; + unsigned64 nl = l->fraction & 0xffffffff; + unsigned64 nh = l->fraction >> 32; + unsigned64 ml = r->fraction & 0xffffffff; + unsigned64 mh = r->fraction >>32; + unsigned64 pp_ll = ml * nl; + unsigned64 pp_hl = mh * nl; + unsigned64 pp_lh = ml * nh; + unsigned64 pp_hh = mh * nh; + unsigned64 res2 = 0; + unsigned64 res0 = 0; + unsigned64 ps_hh__ = pp_hl + pp_lh; + if (ps_hh__ < pp_hl) + res2 += UNSIGNED64 (0x100000000); + pp_hl = (ps_hh__ << 32) & UNSIGNED64 (0xffffffff00000000); + res0 = pp_ll + pp_hl; + if (res0 < pp_ll) + res2++; + res2 += ((ps_hh__ >> 32) & 0xffffffff) + pp_hh; + high = res2; + low = res0; + + f->normal_exp = l->normal_exp + r->normal_exp; + f->sign = l->sign ^ r->sign; + f->class = sim_fpu_class_number; + + /* Input is bounded by [1,2) ; [2^60,2^61) + Output is bounded by [1,4) ; [2^120,2^122) */ + + /* Adjust the exponent according to where the decimal point ended + up in the high 64 bit word. In the source the decimal point + was at NR_FRAC_GUARD. */ + f->normal_exp += NR_FRAC_GUARD + 64 - (NR_FRAC_GUARD * 2); + + /* The high word is bounded according to the above. Consequently + it has never overflowed into IMPLICIT_2. */ + ASSERT (high < LSBIT64 (((NR_FRAC_GUARD + 1) * 2) - 64)); + ASSERT (high >= LSBIT64 ((NR_FRAC_GUARD * 2) - 64)); + ASSERT (LSBIT64 (((NR_FRAC_GUARD + 1) * 2) - 64) < IMPLICIT_1); + + /* Normalize. */ + do + { + f->normal_exp--; + high <<= 1; + if (low & LSBIT64 (63)) + high |= 1; + low <<= 1; + } + while (high < IMPLICIT_1); + + ASSERT (high >= IMPLICIT_1 && high < IMPLICIT_2); + if (low != 0) + { + f->fraction = (high | 1); /* sticky */ + return sim_fpu_status_inexact; + } + else + { + f->fraction = high; + return 0; + } + return 0; + } +} + +INLINE_SIM_FPU (int) +sim_fpu_div (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; + f->class = sim_fpu_class_qnan; + return 0; + } + if (sim_fpu_is_qnan (r)) + { + *f = *r; + f->class = sim_fpu_class_qnan; + return 0; + } + if (sim_fpu_is_infinity (l)) + { + if (sim_fpu_is_infinity (r)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_idi; + } + else + { + *f = *l; + f->sign = l->sign ^ r->sign; + return 0; + } + } + if (sim_fpu_is_zero (l)) + { + if (sim_fpu_is_zero (r)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_zdz; + } + else + { + *f = *l; + f->sign = l->sign ^ r->sign; + return 0; + } + } + if (sim_fpu_is_infinity (r)) + { + *f = sim_fpu_zero; + f->sign = l->sign ^ r->sign; + return 0; + } + if (sim_fpu_is_zero (r)) + { + f->class = sim_fpu_class_infinity; + f->sign = l->sign ^ r->sign; + return sim_fpu_status_invalid_div0; + } + + /* Calculate the mantissa by multiplying both 64bit numbers to get a + 128 bit number. */ + { + /* quotient = ( ( numerator / denominator) + x 2^(numerator exponent - denominator exponent) + */ + unsigned64 numerator; + unsigned64 denominator; + unsigned64 quotient; + unsigned64 bit; + + f->class = sim_fpu_class_number; + f->sign = l->sign ^ r->sign; + f->normal_exp = l->normal_exp - r->normal_exp; + + numerator = l->fraction; + denominator = r->fraction; + + /* Fraction will be less than 1.0 */ + if (numerator < denominator) + { + numerator <<= 1; + f->normal_exp--; + } + ASSERT (numerator >= denominator); + + /* Gain extra precision, already used one spare bit. */ + numerator <<= NR_SPARE; + denominator <<= NR_SPARE; + + /* Does divide one bit at a time. Optimize??? */ + quotient = 0; + bit = (IMPLICIT_1 << NR_SPARE); + while (bit) + { + if (numerator >= denominator) + { + quotient |= bit; + numerator -= denominator; + } + bit >>= 1; + numerator <<= 1; + } + + /* Discard (but save) the extra bits. */ + if ((quotient & LSMASK64 (NR_SPARE -1, 0))) + quotient = (quotient >> NR_SPARE) | 1; + else + quotient = (quotient >> NR_SPARE); + + f->fraction = quotient; + ASSERT (f->fraction >= IMPLICIT_1 && f->fraction < IMPLICIT_2); + if (numerator != 0) + { + f->fraction |= 1; /* Stick remaining bits. */ + return sim_fpu_status_inexact; + } + else + return 0; + } +} + + +INLINE_SIM_FPU (int) +sim_fpu_rem (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; + f->class = sim_fpu_class_qnan; + return 0; + } + if (sim_fpu_is_qnan (r)) + { + *f = *r; + f->class = sim_fpu_class_qnan; + return 0; + } + if (sim_fpu_is_infinity (l)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_irx; + } + if (sim_fpu_is_zero (r)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_div0; + } + if (sim_fpu_is_zero (l)) + { + *f = *l; + return 0; + } + if (sim_fpu_is_infinity (r)) + { + *f = *l; + return 0; + } + { + sim_fpu n, tmp; + + /* Remainder is calculated as l-n*r, where n is l/r rounded to the + nearest integer. The variable n is rounded half even. */ + + sim_fpu_div (&n, l, r); + sim_fpu_round_64 (&n, 0, 0); + + if (n.normal_exp < -1) /* If n looks like zero just return l. */ + { + *f = *l; + return 0; + } + else if (n.class == sim_fpu_class_number + && n.normal_exp <= (NR_FRAC_GUARD)) /* If not too large round. */ + do_normal_round (&n, (NR_FRAC_GUARD) - n.normal_exp, sim_fpu_round_near); + + /* Mark 0's as zero so multiply can detect zero. */ + if (n.fraction == 0) + n.class = sim_fpu_class_zero; + + /* Calculate n*r. */ + sim_fpu_mul (&tmp, &n, r); + sim_fpu_round_64 (&tmp, 0, 0); + + /* Finally calculate l-n*r. */ + sim_fpu_sub (f, l, &tmp); + + return 0; + } +} + + +INLINE_SIM_FPU (int) +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; + } + 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 > anything */ + return 0; + } + if (sim_fpu_is_infinity (r)) + { + if (r->sign) + *f = *l; /* anything > -inf */ + else + *f = *r; /* anything < +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_min (sim_fpu *f, + const sim_fpu *l, + const sim_fpu *r) { - sim_fpu ans; - ans.val = l.val + r.val; - return ans; + 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; + } + 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 (sim_fpu) -sim_fpu_sub (sim_fpu l, - sim_fpu r) +INLINE_SIM_FPU (int) +sim_fpu_neg (sim_fpu *f, + const sim_fpu *r) { - sim_fpu ans; - ans.val = l.val - r.val; - return ans; + 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 (r)) + { + *f = *r; + return 0; + } + *f = *r; + f->sign = !r->sign; + return 0; } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_mul (sim_fpu l, - sim_fpu r) +INLINE_SIM_FPU (int) +sim_fpu_abs (sim_fpu *f, + const sim_fpu *r) { - sim_fpu ans; - ans.val = l.val * r.val; - return ans; + *f = *r; + f->sign = 0; + if (sim_fpu_is_snan (r)) + { + f->class = sim_fpu_class_qnan; + return sim_fpu_status_invalid_snan; + } + return 0; } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_div (sim_fpu l, - sim_fpu r) +INLINE_SIM_FPU (int) +sim_fpu_inv (sim_fpu *f, + const sim_fpu *r) { - sim_fpu ans; - ans.val = l.val / r.val; - return ans; + return sim_fpu_div (f, &sim_fpu_one, r); } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_inv (sim_fpu r) +INLINE_SIM_FPU (int) +sim_fpu_sqrt (sim_fpu *f, + const sim_fpu *r) { - sim_fpu ans; - ans.val = 1 / r.val; - return ans; + if (sim_fpu_is_snan (r)) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_snan; + } + if (sim_fpu_is_qnan (r)) + { + *f = sim_fpu_qnan; + return 0; + } + if (sim_fpu_is_zero (r)) + { + f->class = sim_fpu_class_zero; + f->sign = r->sign; + f->normal_exp = 0; + return 0; + } + if (sim_fpu_is_infinity (r)) + { + if (r->sign) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_sqrt; + } + else + { + f->class = sim_fpu_class_infinity; + f->sign = 0; + f->sign = 0; + return 0; + } + } + if (r->sign) + { + *f = sim_fpu_qnan; + return sim_fpu_status_invalid_sqrt; + } + + /* @(#)e_sqrt.c 5.1 93/09/24 */ + /* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + + /* __ieee754_sqrt(x) + * Return correctly rounded sqrt. + * ------------------------------------------ + * | Use the hardware sqrt if you have one | + * ------------------------------------------ + * Method: + * Bit by bit method using integer arithmetic. (Slow, but portable) + * 1. Normalization + * Scale x to y in [1,4) with even powers of 2: + * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then + * sqrt(x) = 2^k * sqrt(y) + - + - Since: + - sqrt ( x*2^(2m) ) = sqrt(x).2^m ; m even + - sqrt ( x*2^(2m + 1) ) = sqrt(2.x).2^m ; m odd + - Define: + - y = ((m even) ? x : 2.x) + - Then: + - y in [1, 4) ; [IMPLICIT_1,IMPLICIT_4) + - And: + - sqrt (y) in [1, 2) ; [IMPLICIT_1,IMPLICIT_2) + - + * 2. Bit by bit computation + * Let q = sqrt(y) truncated to i bit after binary point (q = 1), + * i 0 + * i+1 2 + * s = 2*q , and y = 2 * ( y - q ). (1) + * i i i i + * + * To compute q from q , one checks whether + * i+1 i + * + * -(i+1) 2 + * (q + 2 ) <= y. (2) + * i + * -(i+1) + * If (2) is false, then q = q ; otherwise q = q + 2 . + * i+1 i i+1 i + * + * With some algebraic manipulation, it is not difficult to see + * that (2) is equivalent to + * -(i+1) + * s + 2 <= y (3) + * i i + * + * The advantage of (3) is that s and y can be computed by + * i i + * the following recurrence formula: + * if (3) is false + * + * s = s , y = y ; (4) + * i+1 i i+1 i + * + - + - NOTE: y = 2*y + - i+1 i + - + * otherwise, + * -i -(i+1) + * s = s + 2 , y = y - s - 2 (5) + * i+1 i i+1 i i + * + - + - -(i+1) + - NOTE: y = 2 (y - s - 2 ) + - i+1 i i + - + * One may easily use induction to prove (4) and (5). + * Note. Since the left hand side of (3) contain only i+2 bits, + * it does not necessary to do a full (53-bit) comparison + * in (3). + * 3. Final rounding + * After generating the 53 bits result, we compute one more bit. + * Together with the remainder, we can decide whether the + * result is exact, bigger than 1/2ulp, or less than 1/2ulp + * (it will never equal to 1/2ulp). + * The rounding mode can be detected by checking whether + * huge + tiny is equal to huge, and whether huge - tiny is + * equal to huge for some floating point number "huge" and "tiny". + * + * Special cases: + * sqrt(+-0) = +-0 ... exact + * sqrt(inf) = inf + * sqrt(-ve) = NaN ... with invalid signal + * sqrt(NaN) = NaN ... with invalid signal for signalling NaN + * + * Other methods : see the appended file at the end of the program below. + *--------------- + */ + + { + /* Generate sqrt(x) bit by bit. */ + unsigned64 y; + unsigned64 q; + unsigned64 s; + unsigned64 b; + + f->class = sim_fpu_class_number; + f->sign = 0; + y = r->fraction; + f->normal_exp = (r->normal_exp >> 1); /* exp = [exp/2] */ + + /* Odd exp, double x to make it even. */ + ASSERT (y >= IMPLICIT_1 && y < IMPLICIT_4); + if ((r->normal_exp & 1)) + { + y += y; + } + ASSERT (y >= IMPLICIT_1 && y < (IMPLICIT_2 << 1)); + + /* Let loop determine first value of s (either 1 or 2) */ + b = IMPLICIT_1; + q = 0; + s = 0; + + while (b) + { + unsigned64 t = s + b; + if (t <= y) + { + s |= (b << 1); + y -= t; + q |= b; + } + y <<= 1; + b >>= 1; + } + + ASSERT (q >= IMPLICIT_1 && q < IMPLICIT_2); + f->fraction = q; + if (y != 0) + { + f->fraction |= 1; /* Stick remaining bits. */ + return sim_fpu_status_inexact; + } + else + return 0; + } } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_sqrt (sim_fpu r) +/* int/long <-> sim_fpu */ + +INLINE_SIM_FPU (int) +sim_fpu_i32to (sim_fpu *f, + signed32 i, + sim_fpu_round round) { - sim_fpu ans; - ans.val = sqrt (r.val); - return ans; + i2fpu (f, i, 0); + return 0; } +INLINE_SIM_FPU (int) +sim_fpu_u32to (sim_fpu *f, + unsigned32 u, + sim_fpu_round round) +{ + u2fpu (f, u, 0); + return 0; +} -/* int/long -> sim_fpu */ +INLINE_SIM_FPU (int) +sim_fpu_i64to (sim_fpu *f, + signed64 i, + sim_fpu_round round) +{ + i2fpu (f, i, 1); + return 0; +} -INLINE_SIM_FPU (sim_fpu) -sim_fpu_i32to (signed32 s) +INLINE_SIM_FPU (int) +sim_fpu_u64to (sim_fpu *f, + unsigned64 u, + sim_fpu_round round) { - sim_fpu ans; - ans.val = s; - return ans; + u2fpu (f, u, 1); + return 0; } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_u32to (unsigned32 s) +INLINE_SIM_FPU (int) +sim_fpu_to32i (signed32 *i, + const sim_fpu *f, + sim_fpu_round round) { - sim_fpu ans; - ans.val = s; - return ans; + signed64 i64; + int status = fpu2i (&i64, f, 0, round); + *i = i64; + return status; } +INLINE_SIM_FPU (int) +sim_fpu_to32u (unsigned32 *u, + const sim_fpu *f, + sim_fpu_round round) +{ + unsigned64 u64; + int status = fpu2u (&u64, f, 0); + *u = u64; + return status; +} -INLINE_SIM_FPU (sim_fpu) -sim_fpu_i64to (signed64 s) +INLINE_SIM_FPU (int) +sim_fpu_to64i (signed64 *i, + const sim_fpu *f, + sim_fpu_round round) { - sim_fpu ans; - ans.val = s; - return ans; + return fpu2i (i, f, 1, round); } -INLINE_SIM_FPU (sim_fpu) -sim_fpu_u64to (unsigned64 s) +INLINE_SIM_FPU (int) +sim_fpu_to64u (unsigned64 *u, + const sim_fpu *f, + sim_fpu_round round) { - sim_fpu ans; - ans.val = s; - return ans; + return fpu2u (u, f, 1); } + /* sim_fpu -> host format */ +#if 0 INLINE_SIM_FPU (float) -sim_fpu_2f (sim_fpu f) +sim_fpu_2f (const sim_fpu *f) { - return f.val; + return fval.d; } +#endif INLINE_SIM_FPU (double) -sim_fpu_2d (sim_fpu s) +sim_fpu_2d (const sim_fpu *s) { - return s.val; + sim_fpu_map val; + 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; } #if 0 -INLINE_SIM_FPU (sim_fpu) -sim_fpu_f2 (float f) +INLINE_SIM_FPU (void) +sim_fpu_f2 (sim_fpu *f, + float s) { - sim_fpu ans; - ans.val = f; - return ans; + sim_fpu_map val; + val.d = s; + unpack_fpu (f, val.i, 1); } #endif -#if 0 -INLINE_SIM_FPU (sim_fpu) -sim_fpu_d2 (double d) +INLINE_SIM_FPU (void) +sim_fpu_d2 (sim_fpu *f, + double d) { - sim_fpu ans; - ans.val = d; - return ans; + sim_fpu_map val; + val.d = d; + unpack_fpu (f, val.i, 1); } -#endif - /* General */ INLINE_SIM_FPU (int) -sim_fpu_is_nan (sim_fpu d) +sim_fpu_is_nan (const sim_fpu *d) +{ + switch (d->class) + { + case sim_fpu_class_qnan: + case sim_fpu_class_snan: + return 1; + default: + return 0; + } +} + +INLINE_SIM_FPU (int) +sim_fpu_is_qnan (const sim_fpu *d) { - return 0; /* FIXME - detect NaN */ + switch (d->class) + { + case sim_fpu_class_qnan: + return 1; + default: + return 0; + } +} + +INLINE_SIM_FPU (int) +sim_fpu_is_snan (const sim_fpu *d) +{ + switch (d->class) + { + case sim_fpu_class_snan: + return 1; + default: + return 0; + } +} + +INLINE_SIM_FPU (int) +sim_fpu_is_zero (const sim_fpu *d) +{ + switch (d->class) + { + case sim_fpu_class_zero: + return 1; + default: + return 0; + } +} + +INLINE_SIM_FPU (int) +sim_fpu_is_infinity (const sim_fpu *d) +{ + switch (d->class) + { + case sim_fpu_class_infinity: + return 1; + default: + return 0; + } +} + +INLINE_SIM_FPU (int) +sim_fpu_is_number (const sim_fpu *d) +{ + switch (d->class) + { + case sim_fpu_class_denorm: + case sim_fpu_class_number: + return 1; + default: + return 0; + } +} + +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) +{ + switch (d->class) + { + case sim_fpu_class_qnan: + return SIM_FPU_IS_QNAN; + case sim_fpu_class_snan: + return SIM_FPU_IS_SNAN; + case sim_fpu_class_infinity: + 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_NNUMBER; + else + return SIM_FPU_IS_PNUMBER; + case sim_fpu_class_denorm: + if (d->sign) + return SIM_FPU_IS_NDENORM; + else + return SIM_FPU_IS_PDENORM; + case sim_fpu_class_zero: + if (d->sign) + return SIM_FPU_IS_NZERO; + else + return SIM_FPU_IS_PZERO; + default: + return -1; + abort (); + } +} + +INLINE_SIM_FPU (int) +sim_fpu_cmp (const sim_fpu *l, const sim_fpu *r) +{ + sim_fpu res; + sim_fpu_sub (&res, l, r); + return sim_fpu_is (&res); +} + +INLINE_SIM_FPU (int) +sim_fpu_is_lt (const sim_fpu *l, const sim_fpu *r) +{ + int status; + sim_fpu_lt (&status, l, r); + return status; +} + +INLINE_SIM_FPU (int) +sim_fpu_is_le (const sim_fpu *l, const sim_fpu *r) +{ + int is; + sim_fpu_le (&is, l, r); + return is; +} + +INLINE_SIM_FPU (int) +sim_fpu_is_eq (const sim_fpu *l, const sim_fpu *r) +{ + int is; + sim_fpu_eq (&is, l, r); + return is; +} + +INLINE_SIM_FPU (int) +sim_fpu_is_ne (const sim_fpu *l, const sim_fpu *r) +{ + int is; + sim_fpu_ne (&is, l, r); + return is; +} + +INLINE_SIM_FPU (int) +sim_fpu_is_ge (const sim_fpu *l, const sim_fpu *r) +{ + int is; + sim_fpu_ge (&is, l, r); + return is; +} + +INLINE_SIM_FPU (int) +sim_fpu_is_gt (const sim_fpu *l, const sim_fpu *r) +{ + int is; + sim_fpu_gt (&is, l, r); + return is; } /* Compare operators */ INLINE_SIM_FPU (int) -sim_fpu_is_lt (sim_fpu l, - sim_fpu r) +sim_fpu_lt (int *is, + const sim_fpu *l, + const sim_fpu *r) { - return (l.val < r.val); + if (!sim_fpu_is_nan (l) && !sim_fpu_is_nan (r)) + { + sim_fpu_map lval; + sim_fpu_map rval; + lval.i = pack_fpu (l, 1); + rval.i = pack_fpu (r, 1); + (*is) = (lval.d < rval.d); + return 0; + } + else if (sim_fpu_is_snan (l) || sim_fpu_is_snan (r)) + { + *is = 0; + return sim_fpu_status_invalid_snan; + } + else + { + *is = 0; + return sim_fpu_status_invalid_qnan; + } } INLINE_SIM_FPU (int) -sim_fpu_is_le (sim_fpu l, - sim_fpu r) +sim_fpu_le (int *is, + const sim_fpu *l, + const sim_fpu *r) { - return (l.val <= r.val); + if (!sim_fpu_is_nan (l) && !sim_fpu_is_nan (r)) + { + sim_fpu_map lval; + sim_fpu_map rval; + lval.i = pack_fpu (l, 1); + rval.i = pack_fpu (r, 1); + *is = (lval.d <= rval.d); + return 0; + } + else if (sim_fpu_is_snan (l) || sim_fpu_is_snan (r)) + { + *is = 0; + return sim_fpu_status_invalid_snan; + } + else + { + *is = 0; + return sim_fpu_status_invalid_qnan; + } } INLINE_SIM_FPU (int) -sim_fpu_is_eq (sim_fpu l, - sim_fpu r) +sim_fpu_eq (int *is, + const sim_fpu *l, + const sim_fpu *r) { - return (l.val == r.val); + if (!sim_fpu_is_nan (l) && !sim_fpu_is_nan (r)) + { + sim_fpu_map lval; + sim_fpu_map rval; + lval.i = pack_fpu (l, 1); + rval.i = pack_fpu (r, 1); + (*is) = (lval.d == rval.d); + return 0; + } + else if (sim_fpu_is_snan (l) || sim_fpu_is_snan (r)) + { + *is = 0; + return sim_fpu_status_invalid_snan; + } + else + { + *is = 0; + return sim_fpu_status_invalid_qnan; + } } INLINE_SIM_FPU (int) -sim_fpu_is_ne (sim_fpu l, - sim_fpu r) +sim_fpu_ne (int *is, + const sim_fpu *l, + const sim_fpu *r) { - return (l.val != r.val); + if (!sim_fpu_is_nan (l) && !sim_fpu_is_nan (r)) + { + sim_fpu_map lval; + sim_fpu_map rval; + lval.i = pack_fpu (l, 1); + rval.i = pack_fpu (r, 1); + (*is) = (lval.d != rval.d); + return 0; + } + else if (sim_fpu_is_snan (l) || sim_fpu_is_snan (r)) + { + *is = 0; + return sim_fpu_status_invalid_snan; + } + else + { + *is = 0; + return sim_fpu_status_invalid_qnan; + } } INLINE_SIM_FPU (int) -sim_fpu_is_ge (sim_fpu l, - sim_fpu r) +sim_fpu_ge (int *is, + const sim_fpu *l, + const sim_fpu *r) { - return (l.val >= r.val); + return sim_fpu_le (is, r, l); } INLINE_SIM_FPU (int) -sim_fpu_is_gt (sim_fpu l, - sim_fpu r) +sim_fpu_gt (int *is, + const sim_fpu *l, + const sim_fpu *r) +{ + return sim_fpu_lt (is, r, l); +} + + +/* A number of useful constants */ + +#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 */ + +INLINE_SIM_FPU (void) +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, digits, print, arg); + print (arg, "*QuietNaN"); + break; + case sim_fpu_class_snan: + print (arg, "0."); + print_bits (f->fraction, NR_FRAC_GUARD - 1, digits, print, arg); + print (arg, "*SignalNaN"); + break; + case sim_fpu_class_zero: + print (arg, "0.0"); + break; + case sim_fpu_class_infinity: + 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, digits, print, arg); + print (arg, "*2^%+d", f->normal_exp); + ASSERT (f->fraction >= IMPLICIT_1); + ASSERT (f->fraction < IMPLICIT_2); + } +} + + +INLINE_SIM_FPU (void) +sim_fpu_print_status (int status, + sim_fpu_print_func *print, + void *arg) { - return (l.val > r.val); + int i = 1; + const char *prefix = ""; + while (status >= i) + { + switch ((sim_fpu_status) (status & i)) + { + case sim_fpu_status_denorm: + print (arg, "%sD", prefix); + break; + case sim_fpu_status_invalid_snan: + print (arg, "%sSNaN", prefix); + break; + case sim_fpu_status_invalid_qnan: + print (arg, "%sQNaN", prefix); + break; + case sim_fpu_status_invalid_isi: + print (arg, "%sISI", prefix); + break; + case sim_fpu_status_invalid_idi: + print (arg, "%sIDI", prefix); + break; + case sim_fpu_status_invalid_zdz: + print (arg, "%sZDZ", prefix); + break; + case sim_fpu_status_invalid_imz: + print (arg, "%sIMZ", prefix); + break; + case sim_fpu_status_invalid_cvi: + print (arg, "%sCVI", prefix); + break; + case sim_fpu_status_invalid_cmp: + print (arg, "%sCMP", prefix); + break; + case sim_fpu_status_invalid_sqrt: + print (arg, "%sSQRT", prefix); + break; + case sim_fpu_status_invalid_irx: + print (arg, "%sIRX", prefix); + break; + case sim_fpu_status_inexact: + print (arg, "%sX", prefix); + break; + case sim_fpu_status_overflow: + print (arg, "%sO", prefix); + break; + case sim_fpu_status_underflow: + print (arg, "%sU", prefix); + break; + case sim_fpu_status_invalid_div0: + print (arg, "%s/", prefix); + break; + case sim_fpu_status_rounded: + print (arg, "%sR", prefix); + break; + } + i <<= 1; + prefix = ","; + } } #endif