Module Name: src Committed By: rin Date: Thu Sep 1 05:56:52 UTC 2022
Modified Files: src/sys/arch/powerpc/fpu: fpu_implode.c Log Message: Further fix for fcti{w,d}{,z}. - Treat {Q,S}NaN correctly. - Set exception bits appropriately. - Introduce round_int(). To generate a diff of this commit: cvs rdiff -u -r1.11 -r1.12 src/sys/arch/powerpc/fpu/fpu_implode.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/powerpc/fpu/fpu_implode.c diff -u src/sys/arch/powerpc/fpu/fpu_implode.c:1.11 src/sys/arch/powerpc/fpu/fpu_implode.c:1.12 --- src/sys/arch/powerpc/fpu/fpu_implode.c:1.11 Thu Sep 1 05:51:51 2022 +++ src/sys/arch/powerpc/fpu/fpu_implode.c Thu Sep 1 05:56:52 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: fpu_implode.c,v 1.11 2022/09/01 05:51:51 rin Exp $ */ +/* $NetBSD: fpu_implode.c,v 1.12 2022/09/01 05:56:52 rin Exp $ */ /* * Copyright (c) 1992, 1993 @@ -46,7 +46,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: fpu_implode.c,v 1.11 2022/09/01 05:51:51 rin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: fpu_implode.c,v 1.12 2022/09/01 05:56:52 rin Exp $"); #include <sys/types.h> #include <sys/systm.h> @@ -62,6 +62,7 @@ __KERNEL_RCSID(0, "$NetBSD: fpu_implode. static int round(struct fpemu *, struct fpn *); static int toinf(struct fpemu *, int); +static int round_int(struct fpn *, int *, int, int, int); /* * Round a number (algorithm from Motorola MC68882 manual, modified for @@ -189,6 +190,40 @@ toinf(struct fpemu *fe, int sign) return (inf); } +static int +round_int(struct fpn *fp, int *cx, int rn, int sign, int odd) +{ + int g, rs; + + g = fp->fp_mant[3] & 0x80000000; + rs = (fp->fp_mant[3] & 0x7fffffff) | fp->fp_sticky; + + if ((g | rs) == 0) + return 0; /* exact */ + + *cx |= FPSCR_XX | FPSCR_FI; + + switch (rn) { + case FSR_RD_RN: + if (g && (rs | odd)) + break; + return 0; + case FSR_RD_RZ: + return 0; + case FSR_RD_RP: + if (!sign) + break; + return 0; + case FSR_RD_RM: + if (sign) + break; + return 0; + } + + *cx |= FPSCR_FR; + return 1; +} + /* * fpn -> int (int value returned as return value). */ @@ -196,10 +231,17 @@ u_int fpu_ftoi(struct fpemu *fe, struct fpn *fp, int rn) { u_int i; - int sign, exp, g, rs; + int sign, exp, cx; sign = fp->fp_sign; + cx = 0; switch (fp->fp_class) { + case FPC_SNAN: + fe->fe_cx |= FPSCR_VXSNAN; + /* FALLTHROUGH */ + case FPC_QNAN: + sign = 1; + break; case FPC_ZERO: return (0); @@ -218,34 +260,15 @@ fpu_ftoi(struct fpemu *fe, struct fpn *f if ((exp = fp->fp_exp) >= 32) break; /* NB: the following includes exp < 0 cases */ - if (fpu_shr(fp, FP_NMANT - 32 - 1 - exp) != 0) - fe->fe_cx |= FPSCR_UX; + (void)fpu_shr(fp, FP_NMANT - 32 - 1 - exp); i = fp->fp_mant[2]; - - g = fp->fp_mant[3] & 0x80000000; - rs = (fp->fp_mant[3] & 0x7fffffff) | fp->fp_sticky; - switch (rn) { - case FSR_RD_RN: - if (g && (rs | (i & 1))) - i++; - break; - case FSR_RD_RZ: - break; - case FSR_RD_RP: - if (!sign && (g | rs)) - i++; - break; - case FSR_RD_RM: - if (sign && (g | rs)) - i++; - break; - } - + i += round_int(fp, &cx, rn, sign, i & 1); if (i >= ((u_int)0x80000000 + sign)) break; + fe->fe_cx |= cx; return (sign ? -i : i); - default: /* Inf, qNaN, sNaN */ + case FPC_INF: break; } /* overflow: replace any inexact exception with invalid */ @@ -260,10 +283,17 @@ uint64_t fpu_ftox(struct fpemu *fe, struct fpn *fp, int rn) { uint64_t i; - int sign, exp, g, rs; + int sign, exp, cx; sign = fp->fp_sign; + cx = 0; switch (fp->fp_class) { + case FPC_SNAN: + fe->fe_cx |= FPSCR_VXSNAN; + /* FALLTHROUGH */ + case FPC_QNAN: + sign = 1; + break; case FPC_ZERO: return (0); @@ -282,34 +312,15 @@ fpu_ftox(struct fpemu *fe, struct fpn *f if ((exp = fp->fp_exp) >= 64) break; /* NB: the following includes exp < 0 cases */ - if (fpu_shr(fp, FP_NMANT - 32 - 1 - exp) != 0) - fe->fe_cx |= FPSCR_UX; + (void)fpu_shr(fp, FP_NMANT - 32 - 1 - exp); i = ((uint64_t)fp->fp_mant[1] << 32) | fp->fp_mant[2]; - - g = fp->fp_mant[3] & 0x80000000; - rs = (fp->fp_mant[3] & 0x7fffffff) | fp->fp_sticky; - switch (rn) { - case FSR_RD_RN: - if (g && (rs | (i & 1))) - i++; - break; - case FSR_RD_RZ: - break; - case FSR_RD_RP: - if (!sign && (g | rs)) - i++; - break; - case FSR_RD_RM: - if (sign && (g | rs)) - i++; - break; - } - + i += round_int(fp, &cx, rn, sign, i & 1); if (i >= ((uint64_t)0x8000000000000000LL + sign)) break; + fe->fe_cx |= cx; return (sign ? -i : i); - default: /* Inf, qNaN, sNaN */ + case FPC_INF: break; } /* overflow: replace any inexact exception with invalid */