📄 sim-fpu.c
字号:
/* 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. *//* Copyright 1994, 1997, 1998, 2003 Free Software Foundation, Inc.This file is free software; you can redistribute it and/or modify itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 2, or (at your option) anylater version.In addition to the permissions in the GNU General Public License, theFree Software Foundation gives you unlimited permission to link thecompiled version of this file with other programs, and to distributethose programs without any restriction coming from the use of thisfile. (The General Public License restrictions do apply in otherrespects; for example, they cover modification of the file, anddistribution when not linked into another program.)This file is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNUGeneral Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; see the file COPYING. If not, write tothe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *//* 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. The original code by Steve Chamberlain, hacked by Mark Eichin and Jim Wilson, all of Cygnus Support. */#ifndef SIM_FPU_C#define SIM_FPU_C#include "sim-basics.h"#include "sim-fpu.h"#include "sim-io.h"#include "sim-assert.h"/* Debugging support. If digits is -1, then print all digits. */static voidprint_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 <SIGN:1><BIASEDEXP:NR_EXPBITS><FRAC:NR_FRACBITS> for both 32 and 64 bit numbers. This number is interpreted as: Normalized (0 < BIASEDEXP && BIASEDEXP < EXPMAX): (sign ? '-' : '+') 1.<FRAC> x 2 ^ (BIASEDEXP - EXPBIAS) Denormalized (0 == BIASEDEXP && FRAC != 0): (sign ? "-" : "+") 0.<FRAC> 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 - <IMPLICIT_1:1><FRACBITS:52><GUARDS:8><PAD:00> 32 bit - <IMPLICIT_1:1><FRACBITS:23><GUARDS:7><PAD:30> */#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)/* 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){ 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; fraction |= QUIET_NAN; break; case sim_fpu_class_snan: sign = src->sign; exp = EXPMAX; /* force fraction to correct class */ fraction = src->fraction; fraction >>= NR_GUARDS; fraction &= ~QUIET_NAN; 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 { /* Non zero fraction, means NaN */ dst->sign = sign; dst->fraction = (fraction << NR_GUARDS); if (fraction >= QUIET_NAN) 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -