Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/powerpc/fpu stfs{,x}{,u}: Switch to conversion algo...



details:   https://anonhg.NetBSD.org/src/rev/e4b1600ff732
branches:  trunk
changeset: 370171:e4b1600ff732
user:      rin <rin%NetBSD.org@localhost>
date:      Tue Sep 20 12:12:42 2022 +0000

description:
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.

diffstat:

 sys/arch/powerpc/fpu/fpu_emu.c |  84 ++++++++++++++++++++++++++++++++++-------
 1 files changed, 69 insertions(+), 15 deletions(-)

diffs (121 lines):

diff -r 137b6f7daf59 -r e4b1600ff732 sys/arch/powerpc/fpu/fpu_emu.c
--- a/sys/arch/powerpc/fpu/fpu_emu.c    Tue Sep 20 10:12:18 2022 +0000
+++ b/sys/arch/powerpc/fpu/fpu_emu.c    Tue Sep 20 12:12:42 2022 +0000
@@ -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 @@
 }
 
 /*
+ * 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 @@
 
                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 */



Home | Main Index | Thread Index | Old Index