From bdfe5c0439d0fdd6db272c2527841bf5640ce5be Mon Sep 17 00:00:00 2001 From: Andrew Cagney Date: Mon, 23 Feb 1998 09:18:30 +0000 Subject: [PATCH] * sim-fpu.h (enum sim_fpu_class): Add sim_fpu_class_denorm. (sim_fpu_fpto, sim_fpu_tofp): Define. --- sim/common/ChangeLog | 5 + sim/common/sim-fpu.c | 47 ++++-- sim/common/sim-fpu.h | 351 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 351 insertions(+), 52 deletions(-) diff --git a/sim/common/ChangeLog b/sim/common/ChangeLog index 605a560849e..67daa634702 100644 --- a/sim/common/ChangeLog +++ b/sim/common/ChangeLog @@ -1,3 +1,8 @@ +Mon Feb 23 13:24:54 1998 Andrew Cagney + + * sim-fpu.h (enum sim_fpu_class): Add sim_fpu_class_denorm. + (sim_fpu_fpto, sim_fpu_tofp): Define. + Fri Feb 20 18:08:51 1998 Andrew Cagney * sim-fpu.c (sim_fpu_cmp): New function. diff --git a/sim/common/sim-fpu.c b/sim/common/sim-fpu.c index 47b7c89c101..184f802492e 100644 --- a/sim/common/sim-fpu.c +++ b/sim/common/sim-fpu.c @@ -163,6 +163,7 @@ typedef union { #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) @@ -202,6 +203,7 @@ pack_fpu (const sim_fpu *src, 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) @@ -293,6 +295,7 @@ pack_fpu (const sim_fpu *src, } +/* 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) { @@ -315,7 +318,7 @@ unpack_fpu (sim_fpu *dst, unsigned64 packed, int is_double) 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) @@ -389,6 +392,7 @@ unpack_fpu (sim_fpu *dst, unsigned64 packed, int is_double) } +/* Convert a floating point into an integer */ STATIC_INLINE_SIM_FPU (int) fpu2i (signed64 *i, const sim_fpu *s, @@ -488,6 +492,7 @@ fpu2i (signed64 *i, return status; } +/* convert an integer into a floating point */ STATIC_INLINE_SIM_FPU (int) i2fpu (sim_fpu *f, signed64 i, int is_64bit) { @@ -559,6 +564,7 @@ i2fpu (sim_fpu *f, signed64 i, int is_64bit) } +/* Convert a floating point into an integer */ STATIC_INLINE_SIM_FPU (int) fpu2u (unsigned64 *u, const sim_fpu *s, int is_64bit) { @@ -615,6 +621,7 @@ 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) { @@ -831,6 +838,7 @@ do_round (sim_fpu *f, return sim_fpu_status_invalid_snan; break; case sim_fpu_class_number: + case sim_fpu_class_denorm: { int status; ASSERT (f->fraction < IMPLICIT_2); @@ -846,10 +854,11 @@ do_round (sim_fpu *f, 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; @@ -858,6 +867,8 @@ do_round (sim_fpu *f, 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)) { @@ -885,7 +896,8 @@ do_round (sim_fpu *f, /* 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; } @@ -1959,6 +1971,7 @@ sim_fpu_is_number (const sim_fpu *d) { switch (d->class) { + case sim_fpu_class_denorm: case sim_fpu_class_number: return 1; default: @@ -1966,6 +1979,18 @@ sim_fpu_is_number (const sim_fpu *d) } } +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_is (const sim_fpu *d) { @@ -1980,21 +2005,14 @@ sim_fpu_is (const sim_fpu *d) 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; @@ -2219,6 +2237,7 @@ sim_fpu_print_fpu (const sim_fpu *f, 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); diff --git a/sim/common/sim-fpu.h b/sim/common/sim-fpu.h index 72f20e1dd84..48710066aed 100644 --- a/sim/common/sim-fpu.h +++ b/sim/common/sim-fpu.h @@ -20,68 +20,343 @@ with this program; if not, write to the Free Software Foundation, Inc., -#ifndef _SIM_FPU_H_ -#define _SIM_FPU_H_ +#ifndef SIM_FPU_H +#define SIM_FPU_H -/* the FPU intermediate type */ + + +/* The FPU intermediate type - this object, passed by reference, + should be treated as opaque. + + + Pragmatics - pass struct by ref: + + The alternatives for this object/interface that were considered + were: a packed 64 bit value; an unpacked structure passed by value; + and an unpacked structure passed by reference. + + The packed 64 bit value was rejected because: it limited the + precision of intermediate values; reasonable performance would only + be achieved when the sim_fpu package was in-lined allowing repeated + unpacking operations to be eliminated. + + For unpacked structures (passed by value and reference), the code + quality of GCC-2.7 (on x86) for each alternative was compared. + Needless to say the results, while better then for a packed 64 bit + object, were still poor (GCC had only limited support for the + optimization of references to structure members). Regardless, the + struct-by-ref alternative achieved better results when compiled + with (better speed) and without (better code density) in-lining. + Here's looking forward to an improved GCC optimizer. + + + Pragmatics - avoid host FP hardware: + + FP operations can be implemented by either: the host's floating + point hardware; or by emulating the FP operations using integer + only routines. This is direct tradeoff between speed, portability + and correctness. + + The two principal reasons for selecting portability and correctness + over speed are: + + 1 - Correctness. The assumption that FP correctness wasn't an + issue for code being run on simulators was wrong. Instead of + running FP tolerant (?) code, simulator users instead typically run + very aggressive FP code sequences. The sole purpose of those + sequences being to test the target ISA's FP implementation. + + 2 - Portability. The host FP implementation is not predictable. A + simulator modeling aggressive FP code sequences using the hosts FPU + relies heavily on the correctness of the hosts FP implementation. + It turns out that such trust can be misplaced. The behavior of + host FP implementations when handling edge conditions such as SNaNs + and exceptions varied widely. + + + */ + + +typedef enum +{ + sim_fpu_class_zero, + sim_fpu_class_snan, + sim_fpu_class_qnan, + sim_fpu_class_number, + sim_fpu_class_denorm, + sim_fpu_class_infinity, +} sim_fpu_class; typedef struct _sim_fpu { - double val; + sim_fpu_class class; + int normal_exp; + int result; + int sign; + unsigned64 fraction; } sim_fpu; -/* Directly map a 32/64bit register quantity into the sim_fpu internal - type ready for various arithmetic and conversion operations. */ -INLINE_SIM_FPU (sim_fpu) sim_fpu_32to (unsigned32 s); -INLINE_SIM_FPU (sim_fpu) sim_fpu_64to (unsigned64 s); +/* Rounding options. + + The value zero (sim_fpu_round_default) for ALU operations indicates + that, when possible, rounding should be avoided. */ -INLINE_SIM_FPU (unsigned32) sim_fpu_to32 (sim_fpu s); -INLINE_SIM_FPU (unsigned64) sim_fpu_to64 (sim_fpu s); +typedef enum +{ + sim_fpu_round_default = 0, + sim_fpu_round_near = 1, + sim_fpu_round_zero = 2, + sim_fpu_round_up = 3, + sim_fpu_round_down = 4, +} sim_fpu_round; -/* Arrithmetic operators */ +/* Options when handling denormalized numbers. */ -INLINE_SIM_FPU (sim_fpu) sim_fpu_add (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (sim_fpu) sim_fpu_sub (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (sim_fpu) sim_fpu_mul (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (sim_fpu) sim_fpu_div (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (sim_fpu) sim_fpu_inv (sim_fpu l); -INLINE_SIM_FPU (sim_fpu) sim_fpu_sqrt (sim_fpu sqr); +typedef enum +{ + sim_fpu_denorm_underflow_inexact = 1, + sim_fpu_denorm_zero = 2, +} sim_fpu_denorm; -/* Conversion of integer value into floating point types */ -INLINE_SIM_FPU (sim_fpu) sim_fpu_i32to (signed32 s); -INLINE_SIM_FPU (sim_fpu) sim_fpu_u32to (unsigned32 s); -INLINE_SIM_FPU (sim_fpu) sim_fpu_i64to (signed64 s); -INLINE_SIM_FPU (sim_fpu) sim_fpu_u64to (unsigned64 s); +/* Status values returned by FPU operators. + When checking the result of an FP sequence (ex 32to, add, single, + to32) the caller may either: check the return value of each FP + operator; or form the union (OR) of the returned values and examine + them once at the end. -/* Conversion of internal sim_fpu type to host float and double - formats - for debuging/tracing */ + FIXME: This facility is still being developed. The choice of + status values returned and their exact meaning may changed in the + future. */ -INLINE_SIM_FPU (float) sim_fpu_2f (sim_fpu f); -INLINE_SIM_FPU (double) sim_fpu_2d (sim_fpu d); +typedef enum +{ + sim_fpu_status_invalid_snan = 1, + sim_fpu_status_invalid_qnan = 2, + sim_fpu_status_invalid_isi = 4, /* (inf - inf) */ + sim_fpu_status_invalid_idi = 8, /* (inf / inf) */ + sim_fpu_status_invalid_zdz = 16, /* (0 / 0) */ + sim_fpu_status_invalid_imz = 32, /* (inf * 0) */ + sim_fpu_status_invalid_cvi = 64, /* convert to integer */ + sim_fpu_status_invalid_div0 = 128, /* (X / 0) */ + sim_fpu_status_invalid_cmp = 256, /* compare */ + sim_fpu_status_invalid_sqrt = 512, + sim_fpu_status_rounded = 1024, + sim_fpu_status_inexact = 2048, + sim_fpu_status_overflow = 4096, + sim_fpu_status_underflow = 8192, + sim_fpu_status_denorm = 16384, +} sim_fpu_status; -#if 0 -INLINE_SIM_FPU (sim_fpu) sim_fpu_f2 (float f); -INLINE_SIM_FPU (sim_fpu) sim_fpu_d2 (double d); + + + +/* Directly map between a 32/64 bit register and the sim_fpu internal + type. + + When converting from the 32/64 bit packed format to the sim_fpu + internal type, the operation is exact. + + When converting from the sim_fpu internal type to 32/64 bit packed + format, the operation may result in a loss of precision. The + configuration macro WITH_FPU_CONVERSION controls this. By default, + silent round to nearest is performed. Alternativly, round up, + round down and round to zero can be performed. In a simulator + emulating exact FPU behavour, sim_fpu_round_{32,64} should be + called before packing the sim_fpu value. */ + +INLINE_SIM_FPU (void) sim_fpu_32to (sim_fpu *f, unsigned32 s); +INLINE_SIM_FPU (void) sim_fpu_232to (sim_fpu *f, unsigned32 h, unsigned32 l); +INLINE_SIM_FPU (void) sim_fpu_64to (sim_fpu *f, unsigned64 d); + +INLINE_SIM_FPU (void) sim_fpu_to32 (unsigned32 *s, const sim_fpu *f); +INLINE_SIM_FPU (void) sim_fpu_to232 (unsigned32 *h, unsigned32 *l, const sim_fpu *f); +INLINE_SIM_FPU (void) sim_fpu_to64 (unsigned64 *d, const sim_fpu *f); + +#if WITH_TARGET_FLOATING_POINT_BITSIZE == 32 +#define sim_fpu_tofp sim_fpu_to32 +#define sim_fpu_fpto sim_fpu_32to +#define sim_fpu_round_fp sim_fpu_round_32 +#endif +#if WITH_TARGET_FLOATING_POINT_BITSIZE == 64 +#define sim_fpu_tofp sim_fpu_to64 +#define sim_fpu_fpto sim_fpu_64to +#define sim_fpu_round_fp sim_fpu_round_64 #endif -/* Signalling or NonSignalling NaN */ +/* Rounding operators. + + Force an intermediate result to an exact 32/64 bit + representation. */ + +INLINE_SIM_FPU (int) sim_fpu_round_32 (sim_fpu *f, + sim_fpu_round round, + sim_fpu_denorm denorm); +INLINE_SIM_FPU (int) sim_fpu_round_64 (sim_fpu *f, + sim_fpu_round round, + sim_fpu_denorm denorm); + + + +/* Arrithmetic operators. + + FIXME: In the future, additional arguments ROUNDING and BITSIZE may + be added. */ + +INLINE_SIM_FPU (int) sim_fpu_add (sim_fpu *f, + const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_sub (sim_fpu *f, + const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_mul (sim_fpu *f, + const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_div (sim_fpu *f, + const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_neg (sim_fpu *f, + const sim_fpu *a); +INLINE_SIM_FPU (int) sim_fpu_abs (sim_fpu *f, + const sim_fpu *a); +INLINE_SIM_FPU (int) sim_fpu_inv (sim_fpu *f, + const sim_fpu *a); +INLINE_SIM_FPU (int) sim_fpu_sqrt (sim_fpu *f, + const sim_fpu *sqr); + + + +/* Conversion of integer <-> floating point. */ + +INLINE_SIM_FPU (int) sim_fpu_i32to (sim_fpu *f, signed32 i, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_u32to (sim_fpu *f, unsigned32 u, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_i64to (sim_fpu *f, signed64 i, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_u64to (sim_fpu *f, unsigned64 u, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_i232to (sim_fpu *f, signed32 h, signed32 l, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_u232to (sim_fpu *f, unsigned32 h, unsigned32 l, + sim_fpu_round round); + +INLINE_SIM_FPU (int) sim_fpu_to32i (signed32 *i, const sim_fpu *f, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_to32u (unsigned32 *u, const sim_fpu *f, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_to64i (signed64 *i, const sim_fpu *f, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_to64u (unsigned64 *u, const sim_fpu *f, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_to232i (signed64 *h, signed64 *l, const sim_fpu *f, + sim_fpu_round round); +INLINE_SIM_FPU (int) sim_fpu_to232u (unsigned64 *h, unsigned64 *l, const sim_fpu *f, + sim_fpu_round round); + + + +/* Conversion of internal sim_fpu type to host double format. + + For debuging/tracing only. A SNaN is never returned. */ + +/* INLINE_SIM_FPU (float) sim_fpu_2f (const sim_fpu *f); */ +INLINE_SIM_FPU (double) sim_fpu_2d (const sim_fpu *d); + +/* INLINE_SIM_FPU (void) sim_fpu_f2 (sim_fpu *f, float s); */ +INLINE_SIM_FPU (void) sim_fpu_d2 (sim_fpu *f, double d); + + + +/* Specific number classes. + + NB: When either a 32/64 bit floating points is converted to + internal format, or an internal format number is rounded to 32/64 + bit precision, a special marker is retained that indicates that the + value was normalized. For such numbers both is_number and + is_denorm will return true. */ + +INLINE_SIM_FPU (int) sim_fpu_is_nan (const sim_fpu *s); /* 1 => SNaN or QNaN */ +INLINE_SIM_FPU (int) sim_fpu_is_snan (const sim_fpu *s); /* 1 => SNaN */ +INLINE_SIM_FPU (int) sim_fpu_is_qnan (const sim_fpu *s); /* 1 => QNaN */ + +INLINE_SIM_FPU (int) sim_fpu_is_zero (const sim_fpu *s); +INLINE_SIM_FPU (int) sim_fpu_is_infinity (const sim_fpu *s); +INLINE_SIM_FPU (int) sim_fpu_is_number (const sim_fpu *s); /* !zero */ +INLINE_SIM_FPU (int) sim_fpu_is_denorm (const sim_fpu *s); /* !zero */ + + +/* Specific comparison operators + + The comparison operators set *IS to zero and return a nonzero + result for NaNs et.al. */ + +INLINE_SIM_FPU (int) sim_fpu_lt (int *is, const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_le (int *is, const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_eq (int *is, const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_ne (int *is, const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_ge (int *is, const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_gt (int *is, const sim_fpu *l, const sim_fpu *r); + +INLINE_SIM_FPU (int) sim_fpu_is_lt (const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_is_le (const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_is_eq (const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_is_ne (const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_is_ge (const sim_fpu *l, const sim_fpu *r); +INLINE_SIM_FPU (int) sim_fpu_is_gt (const sim_fpu *l, const sim_fpu *r); + + + +/* General number class and comparison operators. + + The result of the comparison is indicated by returning one of the + values below. Efficient emulation of a target FP compare + instruction can be achieved by redefining the values below to match + corresponding target FP status bits. + + For instance. SIM_FPU_QNAN may be redefined to be the bit + `INVALID' while SIM_FPU_NINF might be redefined as the bits + `NEGATIVE | INFINITY | VALID'. */ + +#ifndef SIM_FPU_IS_SNAN +enum { + SIM_FPU_IS_SNAN = 1, /* Noisy not-a-number */ + SIM_FPU_IS_QNAN = 2, /* Quite not-a-number */ + SIM_FPU_IS_NINF = 3, /* -infinity */ + SIM_FPU_IS_PINF = 4, /* +infinity */ + SIM_FPU_IS_NNUMBER = 5, /* -number - [ -MAX .. -MIN ] */ + SIM_FPU_IS_PNUMBER = 6, /* +number - [ +MIN .. +MAX ] */ + SIM_FPU_IS_NDENORM = 7, /* -denorm - ( MIN .. 0 ) */ + SIM_FPU_IS_PDENORM = 8, /* +denorm - ( 0 .. MIN ) */ + SIM_FPU_IS_NZERO = 9, /* -0 */ + SIM_FPU_IS_PZERO = 10, /* +0 */ +}; +#endif + +INLINE_SIM_FPU (int) sim_fpu_is (const sim_fpu *l); +INLINE_SIM_FPU (int) sim_fpu_cmp (const sim_fpu *l, const sim_fpu *r); + + + +/* A constant of useful numbers */ + +extern const sim_fpu sim_fpu_zero; +extern const sim_fpu sim_fpu_one; +extern const sim_fpu sim_fpu_two; +extern const sim_fpu sim_fpu_qnan; + -INLINE_SIM_FPU (int) sim_fpu_is_nan (sim_fpu s); +/* For debugging */ +typedef void sim_fpu_print_func (void *, char *, ...); -/* compare l with r; return <0, ==0, >0 accordingly */ +INLINE_SIM_FPU (void) sim_fpu_print_fpu (const sim_fpu *f, + sim_fpu_print_func *print, + void *arg); -INLINE_SIM_FPU (int) sim_fpu_is_lt (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (int) sim_fpu_is_le (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (int) sim_fpu_is_eq (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (int) sim_fpu_is_ne (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (int) sim_fpu_is_ge (sim_fpu l, sim_fpu r); -INLINE_SIM_FPU (int) sim_fpu_is_gt (sim_fpu l, sim_fpu r); +INLINE_SIM_FPU (void) sim_fpu_print_status (int status, + sim_fpu_print_func *print, + void *arg); #endif -- 2.30.2