Module Name:    src
Committed By:   rin
Date:           Tue Sep 20 12:12:42 UTC 2022

Modified Files:
        src/sys/arch/powerpc/fpu: fpu_emu.c

Log Message:
stfs{,x}{,u}: Switch to conversion algorithm specified by Power ISA.

The ISA specifies algorithm for most bit patterns in double format, that
are not representable in float. I believe that sane people do not rely on
such a specification detail, but *REAL* programmers may utilize it ;)

Instead of complicating fpu_explode(), single-purpose helper function,
fpu_to_single(), is introduced. See comment therein for more details.


To generate a diff of this commit:
cvs rdiff -u -r1.58 -r1.59 src/sys/arch/powerpc/fpu/fpu_emu.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_emu.c
diff -u src/sys/arch/powerpc/fpu/fpu_emu.c:1.58 src/sys/arch/powerpc/fpu/fpu_emu.c:1.59
--- src/sys/arch/powerpc/fpu/fpu_emu.c:1.58	Thu Sep 15 14:25:28 2022
+++ src/sys/arch/powerpc/fpu/fpu_emu.c	Tue Sep 20 12:12:42 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu_emu.c,v 1.58 2022/09/15 14:25:28 rin Exp $ */
+/*	$NetBSD: fpu_emu.c,v 1.59 2022/09/20 12:12:42 rin Exp $ */
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fpu_emu.c,v 1.58 2022/09/15 14:25:28 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fpu_emu.c,v 1.59 2022/09/20 12:12:42 rin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -283,6 +283,56 @@ success:
 }
 
 /*
+ * fpu_to_single(): Helper function for stfs{,u}{,x}.
+ *
+ * Single-precision (float) data is internally represented in
+ * double-precision (double) format in floating-point registers (FRs).
+ * Even though double value cannot be translated into float format in
+ * general, Power ISA (2.0.3--3.1) specify conversion algorithm when
+ * stored to memory (see Sec. 4.6.3):
+ *
+ *  - Extra fraction bits are truncated regardless of rounding mode.
+ *  - When magnitude is larger than the maximum number in float format,
+ *    bits 63--62 and 58--29 are mechanically copied into bits 31--0.
+ *  - When magnitude is representable as denormalized number in float
+ *    format, it is stored as normalized double value in FRs;
+ *    denormalization is required in this case.
+ *  - When magnitude is smaller than the minimum denormalized number in
+ *    float format, the result is undefined. For G5 (790MP Rev 1.1),
+ *    (sign | 0) seems to be stored. For G4 and prior, some ``random''
+ *    garbage is stored in exponent. We mimic G5 for now.
+ */
+static uint32_t
+fpu_to_single(uint64_t reg)
+{
+	uint32_t sign, frac, word;
+	int exp, shift;
+
+	sign = (reg & __BIT(63)) >> 32;
+	exp = __SHIFTOUT(reg, __BITS(62, 52)) - 1023;
+	if (exp > -127 || (reg & ~__BIT(63)) == 0) {
+		/*
+		 * No denormalization required: normalized, zero, inf, NaN,
+		 * or numbers larger than MAXFLOAT (see comment above).
+		 *
+		 * Note that MSB and 7-LSBs in exponent are same for double
+		 * and float formats in this case.
+		 */
+		word =  ((reg & __BIT(62)) >> 32) |
+		    __SHIFTOUT(reg, __BITS(58, 52) | __BITS(51, 29));
+	} else if (exp <= -127 && exp >= -149) {
+		/* Denormalized. */
+		shift = - 126 - exp; /* 1 ... 23 */
+		frac = __SHIFTOUT(__BIT(52) | reg, __BITS(52, 29 + shift));
+		word = /* __SHIFTIN(0, __BITS(30, 23)) | */ frac;
+	} else {
+		/* Undefined. Mimic G5 for now. */
+		word = 0;
+	}
+	return sign | word;
+}
+
+/*
  * Execute an FPU instruction (one that runs entirely in the FPU; not
  * FBfcc or STF, for instance).  On return, fe->fe_fs->fs_fsr will be
  * modified to reflect the setting the hardware would have left.
@@ -411,28 +461,32 @@ fpu_execute(struct trapframe *tf, struct
 
 		if (store) {
 			/* Store */
+			uint32_t word;
+			const void *kaddr;
+
 			FPU_EMU_EVCNT_INCR(fpstore);
 			if (type != FTYPE_DBL) {
-				uint64_t buf;
-
+				/*
+				 * As Power ISA specifies conversion algorithm
+				 * for store floating-point single insns, we
+				 * cannot use fpu_explode() and _implode() here.
+				 * See fpu_to_single() and comment therein for
+				 * more details.
+				 */
 				DPRINTF(FPE_INSN,
 					("fpu_execute: Store SNG at %p\n",
 						(void *)addr));
-				fpu_explode(fe, fp = &fe->fe_f1, FTYPE_DBL,
-				    FR(rt));
-				fpu_implode(fe, fp, type, &buf);
-				if (copyout(&buf, (void *)addr, size)) {
-					fe->fe_addr = addr;
-					return (FAULT);
-				}
+				word = fpu_to_single(FR(rt));
+				kaddr = &word;
 			} else {
 				DPRINTF(FPE_INSN,
 					("fpu_execute: Store DBL at %p\n",
 						(void *)addr));
-				if (copyout(&FR(rt), (void *)addr, size)) {
-					fe->fe_addr = addr;
-					return (FAULT);
-				}
+				kaddr = &FR(rt);
+			}
+			if (copyout(kaddr, (void *)addr, size)) {
+				fe->fe_addr = addr;
+				return (FAULT);
 			}
 		} else {
 			/* Load */

Reply via email to