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