diff options
Diffstat (limited to 'arch/mips/math-emu/ieee754dp.c')
-rw-r--r-- | arch/mips/math-emu/ieee754dp.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/arch/mips/math-emu/ieee754dp.c b/arch/mips/math-emu/ieee754dp.c new file mode 100644 index 0000000000..07ef146e20 --- /dev/null +++ b/arch/mips/math-emu/ieee754dp.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEEE754 floating point arithmetic + * double precision: common utilities + */ +/* + * MIPS floating point support + * Copyright (C) 1994-2000 Algorithmics Ltd. + */ + +#include <linux/compiler.h> + +#include "ieee754dp.h" + +int ieee754dp_class(union ieee754dp x) +{ + COMPXDP; + EXPLODEXDP; + return xc; +} + +static inline int ieee754dp_isnan(union ieee754dp x) +{ + return ieee754_class_nan(ieee754dp_class(x)); +} + +static inline int ieee754dp_issnan(union ieee754dp x) +{ + int qbit; + + assert(ieee754dp_isnan(x)); + qbit = (DPMANT(x) & DP_MBIT(DP_FBITS - 1)) == DP_MBIT(DP_FBITS - 1); + return ieee754_csr.nan2008 ^ qbit; +} + + +/* + * Raise the Invalid Operation IEEE 754 exception + * and convert the signaling NaN supplied to a quiet NaN. + */ +union ieee754dp __cold ieee754dp_nanxcpt(union ieee754dp r) +{ + assert(ieee754dp_issnan(r)); + + ieee754_setcx(IEEE754_INVALID_OPERATION); + if (ieee754_csr.nan2008) { + DPMANT(r) |= DP_MBIT(DP_FBITS - 1); + } else { + DPMANT(r) &= ~DP_MBIT(DP_FBITS - 1); + if (!ieee754dp_isnan(r)) + DPMANT(r) |= DP_MBIT(DP_FBITS - 2); + } + + return r; +} + +static u64 ieee754dp_get_rounding(int sn, u64 xm) +{ + /* inexact must round of 3 bits + */ + if (xm & (DP_MBIT(3) - 1)) { + switch (ieee754_csr.rm) { + case FPU_CSR_RZ: + break; + case FPU_CSR_RN: + xm += 0x3 + ((xm >> 3) & 1); + /* xm += (xm&0x8)?0x4:0x3 */ + break; + case FPU_CSR_RU: /* toward +Infinity */ + if (!sn) /* ?? */ + xm += 0x8; + break; + case FPU_CSR_RD: /* toward -Infinity */ + if (sn) /* ?? */ + xm += 0x8; + break; + } + } + return xm; +} + + +/* generate a normal/denormal number with over,under handling + * sn is sign + * xe is an unbiased exponent + * xm is 3bit extended precision value. + */ +union ieee754dp ieee754dp_format(int sn, int xe, u64 xm) +{ + assert(xm); /* we don't gen exact zeros (probably should) */ + + assert((xm >> (DP_FBITS + 1 + 3)) == 0); /* no excess */ + assert(xm & (DP_HIDDEN_BIT << 3)); + + if (xe < DP_EMIN) { + /* strip lower bits */ + int es = DP_EMIN - xe; + + if (ieee754_csr.nod) { + ieee754_setcx(IEEE754_UNDERFLOW); + ieee754_setcx(IEEE754_INEXACT); + + switch(ieee754_csr.rm) { + case FPU_CSR_RN: + case FPU_CSR_RZ: + return ieee754dp_zero(sn); + case FPU_CSR_RU: /* toward +Infinity */ + if (sn == 0) + return ieee754dp_min(0); + else + return ieee754dp_zero(1); + case FPU_CSR_RD: /* toward -Infinity */ + if (sn == 0) + return ieee754dp_zero(0); + else + return ieee754dp_min(1); + } + } + + if (xe == DP_EMIN - 1 && + ieee754dp_get_rounding(sn, xm) >> (DP_FBITS + 1 + 3)) + { + /* Not tiny after rounding */ + ieee754_setcx(IEEE754_INEXACT); + xm = ieee754dp_get_rounding(sn, xm); + xm >>= 1; + /* Clear grs bits */ + xm &= ~(DP_MBIT(3) - 1); + xe++; + } + else { + /* sticky right shift es bits + */ + xm = XDPSRS(xm, es); + xe += es; + assert((xm & (DP_HIDDEN_BIT << 3)) == 0); + assert(xe == DP_EMIN); + } + } + if (xm & (DP_MBIT(3) - 1)) { + ieee754_setcx(IEEE754_INEXACT); + if ((xm & (DP_HIDDEN_BIT << 3)) == 0) { + ieee754_setcx(IEEE754_UNDERFLOW); + } + + /* inexact must round of 3 bits + */ + xm = ieee754dp_get_rounding(sn, xm); + /* adjust exponent for rounding add overflowing + */ + if (xm >> (DP_FBITS + 3 + 1)) { + /* add causes mantissa overflow */ + xm >>= 1; + xe++; + } + } + /* strip grs bits */ + xm >>= 3; + + assert((xm >> (DP_FBITS + 1)) == 0); /* no excess */ + assert(xe >= DP_EMIN); + + if (xe > DP_EMAX) { + ieee754_setcx(IEEE754_OVERFLOW); + ieee754_setcx(IEEE754_INEXACT); + /* -O can be table indexed by (rm,sn) */ + switch (ieee754_csr.rm) { + case FPU_CSR_RN: + return ieee754dp_inf(sn); + case FPU_CSR_RZ: + return ieee754dp_max(sn); + case FPU_CSR_RU: /* toward +Infinity */ + if (sn == 0) + return ieee754dp_inf(0); + else + return ieee754dp_max(1); + case FPU_CSR_RD: /* toward -Infinity */ + if (sn == 0) + return ieee754dp_max(0); + else + return ieee754dp_inf(1); + } + } + /* gen norm/denorm/zero */ + + if ((xm & DP_HIDDEN_BIT) == 0) { + /* we underflow (tiny/zero) */ + assert(xe == DP_EMIN); + if (ieee754_csr.mx & IEEE754_UNDERFLOW) + ieee754_setcx(IEEE754_UNDERFLOW); + return builddp(sn, DP_EMIN - 1 + DP_EBIAS, xm); + } else { + assert((xm >> (DP_FBITS + 1)) == 0); /* no excess */ + assert(xm & DP_HIDDEN_BIT); + + return builddp(sn, xe + DP_EBIAS, xm & ~DP_HIDDEN_BIT); + } +} |