+
+
+/* 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)
+{
+ 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 (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. */