| 1 | /* Simulator Floating-point support. |
| 2 | Copyright (C) 1997-1998 Free Software Foundation, Inc. |
| 3 | Contributed by Cygnus Support. |
| 4 | |
| 5 | This file is part of GDB, the GNU debugger. |
| 6 | |
| 7 | This program is free software; you can redistribute it and/or modify |
| 8 | it under the terms of the GNU General Public License as published by |
| 9 | the Free Software Foundation; either version 2, or (at your option) |
| 10 | any later version. |
| 11 | |
| 12 | This program is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | GNU General Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU General Public License along |
| 18 | with this program; if not, write to the Free Software Foundation, Inc., |
| 19 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| 20 | |
| 21 | |
| 22 | |
| 23 | #ifndef SIM_FPU_H |
| 24 | #define SIM_FPU_H |
| 25 | |
| 26 | |
| 27 | |
| 28 | /* The FPU intermediate type - this object, passed by reference, |
| 29 | should be treated as opaque. |
| 30 | |
| 31 | |
| 32 | Pragmatics - pass struct by ref: |
| 33 | |
| 34 | The alternatives for this object/interface that were considered |
| 35 | were: a packed 64 bit value; an unpacked structure passed by value; |
| 36 | and an unpacked structure passed by reference. |
| 37 | |
| 38 | The packed 64 bit value was rejected because: it limited the |
| 39 | precision of intermediate values; reasonable performance would only |
| 40 | be achieved when the sim_fpu package was in-lined allowing repeated |
| 41 | unpacking operations to be eliminated. |
| 42 | |
| 43 | For unpacked structures (passed by value and reference), the code |
| 44 | quality of GCC-2.7 (on x86) for each alternative was compared. |
| 45 | Needless to say the results, while better then for a packed 64 bit |
| 46 | object, were still poor (GCC had only limited support for the |
| 47 | optimization of references to structure members). Regardless, the |
| 48 | struct-by-ref alternative achieved better results when compiled |
| 49 | with (better speed) and without (better code density) in-lining. |
| 50 | Here's looking forward to an improved GCC optimizer. |
| 51 | |
| 52 | |
| 53 | Pragmatics - avoid host FP hardware: |
| 54 | |
| 55 | FP operations can be implemented by either: the host's floating |
| 56 | point hardware; or by emulating the FP operations using integer |
| 57 | only routines. This is direct tradeoff between speed, portability |
| 58 | and correctness. |
| 59 | |
| 60 | The two principal reasons for selecting portability and correctness |
| 61 | over speed are: |
| 62 | |
| 63 | 1 - Correctness. The assumption that FP correctness wasn't an |
| 64 | issue for code being run on simulators was wrong. Instead of |
| 65 | running FP tolerant (?) code, simulator users instead typically run |
| 66 | very aggressive FP code sequences. The sole purpose of those |
| 67 | sequences being to test the target ISA's FP implementation. |
| 68 | |
| 69 | 2 - Portability. The host FP implementation is not predictable. A |
| 70 | simulator modeling aggressive FP code sequences using the hosts FPU |
| 71 | relies heavily on the correctness of the hosts FP implementation. |
| 72 | It turns out that such trust can be misplaced. The behavior of |
| 73 | host FP implementations when handling edge conditions such as SNaNs |
| 74 | and exceptions varied widely. |
| 75 | |
| 76 | |
| 77 | */ |
| 78 | |
| 79 | |
| 80 | typedef enum |
| 81 | { |
| 82 | sim_fpu_class_zero, |
| 83 | sim_fpu_class_snan, |
| 84 | sim_fpu_class_qnan, |
| 85 | sim_fpu_class_number, |
| 86 | sim_fpu_class_denorm, |
| 87 | sim_fpu_class_infinity, |
| 88 | } sim_fpu_class; |
| 89 | |
| 90 | typedef struct _sim_fpu { |
| 91 | sim_fpu_class class; |
| 92 | int sign; |
| 93 | unsigned64 fraction; |
| 94 | int normal_exp; |
| 95 | } sim_fpu; |
| 96 | |
| 97 | |
| 98 | |
| 99 | /* Rounding options. |
| 100 | |
| 101 | The value zero (sim_fpu_round_default) for ALU operations indicates |
| 102 | that, when possible, rounding should be avoided. */ |
| 103 | |
| 104 | typedef enum |
| 105 | { |
| 106 | sim_fpu_round_default = 0, |
| 107 | sim_fpu_round_near = 1, |
| 108 | sim_fpu_round_zero = 2, |
| 109 | sim_fpu_round_up = 3, |
| 110 | sim_fpu_round_down = 4, |
| 111 | } sim_fpu_round; |
| 112 | |
| 113 | |
| 114 | /* Options when handling denormalized numbers. */ |
| 115 | |
| 116 | typedef enum |
| 117 | { |
| 118 | sim_fpu_denorm_default = 0, |
| 119 | sim_fpu_denorm_underflow_inexact = 1, |
| 120 | sim_fpu_denorm_zero = 2, |
| 121 | } sim_fpu_denorm; |
| 122 | |
| 123 | |
| 124 | |
| 125 | /* Status values returned by FPU operators. |
| 126 | |
| 127 | When checking the result of an FP sequence (ex 32to, add, single, |
| 128 | to32) the caller may either: check the return value of each FP |
| 129 | operator; or form the union (OR) of the returned values and examine |
| 130 | them once at the end. |
| 131 | |
| 132 | FIXME: This facility is still being developed. The choice of |
| 133 | status values returned and their exact meaning may changed in the |
| 134 | future. */ |
| 135 | |
| 136 | typedef enum |
| 137 | { |
| 138 | sim_fpu_status_invalid_snan = 1, |
| 139 | sim_fpu_status_invalid_qnan = 2, |
| 140 | sim_fpu_status_invalid_isi = 4, /* (inf - inf) */ |
| 141 | sim_fpu_status_invalid_idi = 8, /* (inf / inf) */ |
| 142 | sim_fpu_status_invalid_zdz = 16, /* (0 / 0) */ |
| 143 | sim_fpu_status_invalid_imz = 32, /* (inf * 0) */ |
| 144 | sim_fpu_status_invalid_cvi = 64, /* convert to integer */ |
| 145 | sim_fpu_status_invalid_div0 = 128, /* (X / 0) */ |
| 146 | sim_fpu_status_invalid_cmp = 256, /* compare */ |
| 147 | sim_fpu_status_invalid_sqrt = 512, |
| 148 | sim_fpu_status_rounded = 1024, |
| 149 | sim_fpu_status_inexact = 2048, |
| 150 | sim_fpu_status_overflow = 4096, |
| 151 | sim_fpu_status_underflow = 8192, |
| 152 | sim_fpu_status_denorm = 16384, |
| 153 | } sim_fpu_status; |
| 154 | |
| 155 | |
| 156 | |
| 157 | |
| 158 | /* Directly map between a 32/64 bit register and the sim_fpu internal |
| 159 | type. |
| 160 | |
| 161 | When converting from the 32/64 bit packed format to the sim_fpu |
| 162 | internal type, the operation is exact. |
| 163 | |
| 164 | When converting from the sim_fpu internal type to 32/64 bit packed |
| 165 | format, the operation may result in a loss of precision. The |
| 166 | configuration macro WITH_FPU_CONVERSION controls this. By default, |
| 167 | silent round to nearest is performed. Alternatively, round up, |
| 168 | round down and round to zero can be performed. In a simulator |
| 169 | emulating exact FPU behavior, sim_fpu_round_{32,64} should be |
| 170 | called before packing the sim_fpu value. */ |
| 171 | |
| 172 | INLINE_SIM_FPU (void) sim_fpu_32to (sim_fpu *f, unsigned32 s); |
| 173 | INLINE_SIM_FPU (void) sim_fpu_232to (sim_fpu *f, unsigned32 h, unsigned32 l); |
| 174 | INLINE_SIM_FPU (void) sim_fpu_64to (sim_fpu *f, unsigned64 d); |
| 175 | |
| 176 | INLINE_SIM_FPU (void) sim_fpu_to32 (unsigned32 *s, const sim_fpu *f); |
| 177 | INLINE_SIM_FPU (void) sim_fpu_to232 (unsigned32 *h, unsigned32 *l, const sim_fpu *f); |
| 178 | INLINE_SIM_FPU (void) sim_fpu_to64 (unsigned64 *d, const sim_fpu *f); |
| 179 | |
| 180 | |
| 181 | /* Create a sim_fpu struct using raw information. (FRACTION & LSMASK |
| 182 | (PRECISION-1, 0)) is assumed to contain the fraction part of the |
| 183 | floating-point number. The leading bit LSBIT (PRECISION) is always |
| 184 | implied. The number created can be represented by: |
| 185 | |
| 186 | (SIGN ? "-" : "+") "1." FRACTION{PRECISION-1,0} X 2 ^ NORMAL_EXP> |
| 187 | |
| 188 | You can not specify zero using this function. */ |
| 189 | |
| 190 | INLINE_SIM_FPU (void) sim_fpu_fractionto (sim_fpu *f, int sign, int normal_exp, unsigned64 fraction, int precision); |
| 191 | |
| 192 | /* Reverse operation. If S is a non-zero number, discards the implied |
| 193 | leading one and returns PRECISION fraction bits. No rounding is |
| 194 | performed. */ |
| 195 | INLINE_SIM_FPU (unsigned64) sim_fpu_tofraction (const sim_fpu *s, int precision); |
| 196 | |
| 197 | |
| 198 | |
| 199 | /* Rounding operators. |
| 200 | |
| 201 | Force an intermediate result to an exact 32/64 bit |
| 202 | representation. */ |
| 203 | |
| 204 | INLINE_SIM_FPU (int) sim_fpu_round_32 (sim_fpu *f, |
| 205 | sim_fpu_round round, |
| 206 | sim_fpu_denorm denorm); |
| 207 | INLINE_SIM_FPU (int) sim_fpu_round_64 (sim_fpu *f, |
| 208 | sim_fpu_round round, |
| 209 | sim_fpu_denorm denorm); |
| 210 | |
| 211 | |
| 212 | |
| 213 | /* Arithmetic operators. |
| 214 | |
| 215 | FIXME: In the future, additional arguments ROUNDING and BITSIZE may |
| 216 | be added. */ |
| 217 | |
| 218 | typedef int (sim_fpu_op1) (sim_fpu *f, |
| 219 | const sim_fpu *l); |
| 220 | typedef int (sim_fpu_op2) (sim_fpu *f, |
| 221 | const sim_fpu *l, |
| 222 | const sim_fpu *r); |
| 223 | |
| 224 | INLINE_SIM_FPU (int) sim_fpu_add (sim_fpu *f, |
| 225 | const sim_fpu *l, const sim_fpu *r); |
| 226 | INLINE_SIM_FPU (int) sim_fpu_sub (sim_fpu *f, |
| 227 | const sim_fpu *l, const sim_fpu *r); |
| 228 | INLINE_SIM_FPU (int) sim_fpu_mul (sim_fpu *f, |
| 229 | const sim_fpu *l, const sim_fpu *r); |
| 230 | INLINE_SIM_FPU (int) sim_fpu_div (sim_fpu *f, |
| 231 | const sim_fpu *l, const sim_fpu *r); |
| 232 | INLINE_SIM_FPU (int) sim_fpu_max (sim_fpu *f, |
| 233 | const sim_fpu *l, const sim_fpu *r); |
| 234 | INLINE_SIM_FPU (int) sim_fpu_min (sim_fpu *f, |
| 235 | const sim_fpu *l, const sim_fpu *r); |
| 236 | INLINE_SIM_FPU (int) sim_fpu_neg (sim_fpu *f, |
| 237 | const sim_fpu *a); |
| 238 | INLINE_SIM_FPU (int) sim_fpu_abs (sim_fpu *f, |
| 239 | const sim_fpu *a); |
| 240 | INLINE_SIM_FPU (int) sim_fpu_inv (sim_fpu *f, |
| 241 | const sim_fpu *a); |
| 242 | INLINE_SIM_FPU (int) sim_fpu_sqrt (sim_fpu *f, |
| 243 | const sim_fpu *sqr); |
| 244 | |
| 245 | |
| 246 | |
| 247 | /* Conversion of integer <-> floating point. */ |
| 248 | |
| 249 | INLINE_SIM_FPU (int) sim_fpu_i32to (sim_fpu *f, signed32 i, |
| 250 | sim_fpu_round round); |
| 251 | INLINE_SIM_FPU (int) sim_fpu_u32to (sim_fpu *f, unsigned32 u, |
| 252 | sim_fpu_round round); |
| 253 | INLINE_SIM_FPU (int) sim_fpu_i64to (sim_fpu *f, signed64 i, |
| 254 | sim_fpu_round round); |
| 255 | INLINE_SIM_FPU (int) sim_fpu_u64to (sim_fpu *f, unsigned64 u, |
| 256 | sim_fpu_round round); |
| 257 | #if 0 |
| 258 | INLINE_SIM_FPU (int) sim_fpu_i232to (sim_fpu *f, signed32 h, signed32 l, |
| 259 | sim_fpu_round round); |
| 260 | #endif |
| 261 | #if 0 |
| 262 | INLINE_SIM_FPU (int) sim_fpu_u232to (sim_fpu *f, unsigned32 h, unsigned32 l, |
| 263 | sim_fpu_round round); |
| 264 | #endif |
| 265 | |
| 266 | INLINE_SIM_FPU (int) sim_fpu_to32i (signed32 *i, const sim_fpu *f, |
| 267 | sim_fpu_round round); |
| 268 | INLINE_SIM_FPU (int) sim_fpu_to32u (unsigned32 *u, const sim_fpu *f, |
| 269 | sim_fpu_round round); |
| 270 | INLINE_SIM_FPU (int) sim_fpu_to64i (signed64 *i, const sim_fpu *f, |
| 271 | sim_fpu_round round); |
| 272 | INLINE_SIM_FPU (int) sim_fpu_to64u (unsigned64 *u, const sim_fpu *f, |
| 273 | sim_fpu_round round); |
| 274 | #if 0 |
| 275 | INLINE_SIM_FPU (int) sim_fpu_to232i (signed64 *h, signed64 *l, const sim_fpu *f, |
| 276 | sim_fpu_round round); |
| 277 | #endif |
| 278 | #if 0 |
| 279 | INLINE_SIM_FPU (int) sim_fpu_to232u (unsigned64 *h, unsigned64 *l, const sim_fpu *f, |
| 280 | sim_fpu_round round); |
| 281 | #endif |
| 282 | |
| 283 | |
| 284 | /* Conversion of internal sim_fpu type to host double format. |
| 285 | |
| 286 | For debugging/tracing only. A SNaN is never returned. */ |
| 287 | |
| 288 | /* INLINE_SIM_FPU (float) sim_fpu_2f (const sim_fpu *f); */ |
| 289 | INLINE_SIM_FPU (double) sim_fpu_2d (const sim_fpu *d); |
| 290 | |
| 291 | /* INLINE_SIM_FPU (void) sim_fpu_f2 (sim_fpu *f, float s); */ |
| 292 | INLINE_SIM_FPU (void) sim_fpu_d2 (sim_fpu *f, double d); |
| 293 | |
| 294 | |
| 295 | |
| 296 | /* Specific number classes. |
| 297 | |
| 298 | NB: When either, a 32/64 bit floating points is converted to |
| 299 | internal format, or an internal format number is rounded to 32/64 |
| 300 | bit precision, a special marker is retained that indicates that the |
| 301 | value was normalized. For such numbers both is_number and |
| 302 | is_denorm return true. */ |
| 303 | |
| 304 | INLINE_SIM_FPU (int) sim_fpu_is_nan (const sim_fpu *s); /* 1 => SNaN or QNaN */ |
| 305 | INLINE_SIM_FPU (int) sim_fpu_is_snan (const sim_fpu *s); /* 1 => SNaN */ |
| 306 | INLINE_SIM_FPU (int) sim_fpu_is_qnan (const sim_fpu *s); /* 1 => QNaN */ |
| 307 | |
| 308 | INLINE_SIM_FPU (int) sim_fpu_is_zero (const sim_fpu *s); |
| 309 | INLINE_SIM_FPU (int) sim_fpu_is_infinity (const sim_fpu *s); |
| 310 | INLINE_SIM_FPU (int) sim_fpu_is_number (const sim_fpu *s); /* !zero */ |
| 311 | INLINE_SIM_FPU (int) sim_fpu_is_denorm (const sim_fpu *s); /* !zero */ |
| 312 | |
| 313 | |
| 314 | |
| 315 | /* Floating point fields */ |
| 316 | |
| 317 | INLINE_SIM_FPU (int) sim_fpu_sign (const sim_fpu *s); |
| 318 | INLINE_SIM_FPU (int) sim_fpu_exp (const sim_fpu *s); |
| 319 | |
| 320 | |
| 321 | |
| 322 | /* Specific comparison operators |
| 323 | |
| 324 | For NaNs et.al., the comparison operators will set IS to zero and |
| 325 | return a nonzero result. */ |
| 326 | |
| 327 | INLINE_SIM_FPU (int) sim_fpu_lt (int *is, const sim_fpu *l, const sim_fpu *r); |
| 328 | INLINE_SIM_FPU (int) sim_fpu_le (int *is, const sim_fpu *l, const sim_fpu *r); |
| 329 | INLINE_SIM_FPU (int) sim_fpu_eq (int *is, const sim_fpu *l, const sim_fpu *r); |
| 330 | INLINE_SIM_FPU (int) sim_fpu_ne (int *is, const sim_fpu *l, const sim_fpu *r); |
| 331 | INLINE_SIM_FPU (int) sim_fpu_ge (int *is, const sim_fpu *l, const sim_fpu *r); |
| 332 | INLINE_SIM_FPU (int) sim_fpu_gt (int *is, const sim_fpu *l, const sim_fpu *r); |
| 333 | |
| 334 | INLINE_SIM_FPU (int) sim_fpu_is_lt (const sim_fpu *l, const sim_fpu *r); |
| 335 | INLINE_SIM_FPU (int) sim_fpu_is_le (const sim_fpu *l, const sim_fpu *r); |
| 336 | INLINE_SIM_FPU (int) sim_fpu_is_eq (const sim_fpu *l, const sim_fpu *r); |
| 337 | INLINE_SIM_FPU (int) sim_fpu_is_ne (const sim_fpu *l, const sim_fpu *r); |
| 338 | INLINE_SIM_FPU (int) sim_fpu_is_ge (const sim_fpu *l, const sim_fpu *r); |
| 339 | INLINE_SIM_FPU (int) sim_fpu_is_gt (const sim_fpu *l, const sim_fpu *r); |
| 340 | |
| 341 | |
| 342 | |
| 343 | /* General number class and comparison operators. |
| 344 | |
| 345 | The result of the comparison is indicated by returning one of the |
| 346 | values below. Efficient emulation of a target FP compare |
| 347 | instruction can be achieved by redefining the values below to match |
| 348 | corresponding target FP status bits. |
| 349 | |
| 350 | For instance. SIM_FPU_QNAN may be redefined to be the bit |
| 351 | `INVALID' while SIM_FPU_NINF might be redefined as the bits |
| 352 | `NEGATIVE | INFINITY | VALID'. */ |
| 353 | |
| 354 | #ifndef SIM_FPU_IS_SNAN |
| 355 | enum { |
| 356 | SIM_FPU_IS_SNAN = 1, /* Noisy not-a-number */ |
| 357 | SIM_FPU_IS_QNAN = 2, /* Quite not-a-number */ |
| 358 | SIM_FPU_IS_NINF = 3, /* -infinity */ |
| 359 | SIM_FPU_IS_PINF = 4, /* +infinity */ |
| 360 | SIM_FPU_IS_NNUMBER = 5, /* -number - [ -MAX .. -MIN ] */ |
| 361 | SIM_FPU_IS_PNUMBER = 6, /* +number - [ +MIN .. +MAX ] */ |
| 362 | SIM_FPU_IS_NDENORM = 7, /* -denorm - ( MIN .. 0 ) */ |
| 363 | SIM_FPU_IS_PDENORM = 8, /* +denorm - ( 0 .. MIN ) */ |
| 364 | SIM_FPU_IS_NZERO = 9, /* -0 */ |
| 365 | SIM_FPU_IS_PZERO = 10, /* +0 */ |
| 366 | }; |
| 367 | #endif |
| 368 | |
| 369 | INLINE_SIM_FPU (int) sim_fpu_is (const sim_fpu *l); |
| 370 | INLINE_SIM_FPU (int) sim_fpu_cmp (const sim_fpu *l, const sim_fpu *r); |
| 371 | |
| 372 | |
| 373 | |
| 374 | /* A constant of useful numbers */ |
| 375 | |
| 376 | extern const sim_fpu sim_fpu_zero; |
| 377 | extern const sim_fpu sim_fpu_one; |
| 378 | extern const sim_fpu sim_fpu_two; |
| 379 | extern const sim_fpu sim_fpu_qnan; |
| 380 | extern const sim_fpu sim_fpu_max32; |
| 381 | extern const sim_fpu sim_fpu_max64; |
| 382 | |
| 383 | |
| 384 | /* Select the applicable functions for the fp_word type */ |
| 385 | |
| 386 | #if WITH_TARGET_FLOATING_POINT_BITSIZE == 32 |
| 387 | #define sim_fpu_tofp sim_fpu_to32 |
| 388 | #define sim_fpu_fpto sim_fpu_32to |
| 389 | #define sim_fpu_round_fp sim_fpu_round_32 |
| 390 | #define sim_fpu_maxfp sim_fpu_max32 |
| 391 | #endif |
| 392 | #if WITH_TARGET_FLOATING_POINT_BITSIZE == 64 |
| 393 | #define sim_fpu_tofp sim_fpu_to64 |
| 394 | #define sim_fpu_fpto sim_fpu_64to |
| 395 | #define sim_fpu_round_fp sim_fpu_round_64 |
| 396 | #define sim_fpu_maxfp sim_fpu_max64 |
| 397 | #endif |
| 398 | |
| 399 | |
| 400 | |
| 401 | /* For debugging */ |
| 402 | |
| 403 | typedef void sim_fpu_print_func (void *, char *, ...); |
| 404 | |
| 405 | /* Print a sim_fpu with full precision. */ |
| 406 | INLINE_SIM_FPU (void) sim_fpu_print_fpu (const sim_fpu *f, |
| 407 | sim_fpu_print_func *print, |
| 408 | void *arg); |
| 409 | |
| 410 | /* Print a sim_fpu with `n' trailing digits. */ |
| 411 | INLINE_SIM_FPU (void) sim_fpu_printn_fpu (const sim_fpu *f, |
| 412 | sim_fpu_print_func *print, |
| 413 | int digits, |
| 414 | void *arg); |
| 415 | |
| 416 | INLINE_SIM_FPU (void) sim_fpu_print_status (int status, |
| 417 | sim_fpu_print_func *print, |
| 418 | void *arg); |
| 419 | |
| 420 | #if H_REVEALS_MODULE_P (SIM_FPU_INLINE) |
| 421 | #include "sim-fpu.c" |
| 422 | #endif |
| 423 | |
| 424 | #endif |