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