📄 sim-fpu.c
字号:
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 (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){ unsigned64 s = pack_fpu (f, 1); *l = s; *h = (s >> 32);}INLINE_SIM_FPU (void)sim_fpu_to64 (unsigned64 *u, const sim_fpu *f){ *u = pack_fpu (f, 1);}INLINE_SIM_FPU (void)sim_fpu_fractionto (sim_fpu *f, int sign, int normal_exp, unsigned64 fraction, int precision){ 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_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){ 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 magnitute */ *f = *l; return sim_fpu_status_inexact; } if (shift <= - NR_FRAC_GUARD) { /* right has much bigger magnitute */ *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 magnitute */ *f = *l; return sim_fpu_status_inexact; } if (shift <= - NR_FRAC_GUARD) { /* right has much bigger magnitute */ *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -