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 */

Reply via email to