Summary: | libc: r354823 riscv64 has a fault in printf() where IEEE754-2008 fp128 data is output wrong | ||||||||
---|---|---|---|---|---|---|---|---|---|
Product: | Base System | Reporter: | Dennis Clarke <dclarke> | ||||||
Component: | kern | Assignee: | freebsd-bugs (Nobody) <bugs> | ||||||
Status: | Closed FIXED | ||||||||
Severity: | Affects Some People | CC: | br, emaste, mhorne | ||||||
Priority: | --- | Keywords: | needs-qa | ||||||
Version: | CURRENT | Flags: | koobs:
maintainer-feedback?
(br) koobs: maintainer-feedback? (emaste) koobs: maintainer-feedback? (mhorne) |
||||||
Hardware: | riscv | ||||||||
OS: | Any | ||||||||
Attachments: |
|
Description
Dennis Clarke
2019-11-19 03:15:06 UTC
Modify bug arch to riscv. Created attachment 209240 [details]
Assemble , compile and link and check results.
Thank you for the report Dennis Could you: - Summarise the specific expected vs actual output - Clarify/confirm whether or not this is a regression (post base r351523 ppc64 / / base r350568 riscv / base r354823 jenkins) and - If a regression, the latest revision that *didn't* exhibit the issue. Bisection here would be perfect, if possible. (In reply to Kubilay Kocak from comment #3) The output should be the valid IEEE754-2008 floating point representation for pi exactly the same as the Solaris 10 sparcv9 server does : ( 1 ) expected output should be in the last lines : b8 01 17 c5 8c 89 69 84 d1 42 44 b5 1f 92 00 40 pi may be 3.1415926535897932384626433832795028e+00 That would be precisely correct for a little endian RISC-V architecture. ( 2 ) actual output is very correct in memory and very wrong printf behavior : b8 01 17 c5 8c 89 69 84 d1 42 44 b5 1f 92 00 40 pi may be 2.0000076405016834831430856216761921e+00 # So I don't think that pi is that close to 2.00000764... :) The other question is have I ever seen this work ? The answer is no. Sadly I have never seen this work correctly. The output here suggests that the printf() routine correctly handles the static format string seen in the assembly listing at LC46 in that we do get the correct number of ascii digits in the correct places however the in memory little endian fp128 data is not handled well. The text [1]"Handbook of Floating-Point Arithmetic", 2nd Ed, pg48, by Muller, J.-M., Brunie, N., de Dinechin, F., Jeannerod, C.-P., Joldes, M., Lefèvre, V., Melquiond, G., Revol, N., Torres, S. states that the 128 bit floating point format should have a single sign bit, a set of 15 exponent bits and then a mantissa or significand of 112 bits wherein there is an implied leading "1" bit for an effective 113 bit significand. The IEEE 754-1985 standard did not define this format whereas the [2]IEEE 754-2008 standard does. This is well implemented in Solaris and OpenSolaris as well as on IBM Power9 systems running Red Hat Enterprise Linux. I have single stepped through the relevant lines of the code on an IBM Power system and I think the issue is with /lib/libc/stdio/vfprintf.c where the in memory data is handled wrongly. I have a theory on why we are seeing a value near 2.0 but have to work on that yet. [1] https://www.springer.com/gp/book/9783319765259 [2] IEEE Computer Society. IEEE Standard for Floating-Point Arithmetic. IEEE Standard 754-2008, August 2008. also ISBN: 978-0-7381-5752-8 https://ieeexplore.ieee.org/document/4610935 The output from the Solaris 10 sparcv9 server is clearly to be big endian however for claity I simply want to point out that the RISC-V architecture is little endian and that the in memory data is precisely correct. Regardless we should never see a value of pi as 2.0000... Merely a follow up here to indicate this problem has been around since at least r350568 : root@callisto:/home/dclarke/foo # uname -apKU FreeBSD callisto 13.0-CURRENT FreeBSD 13.0-CURRENT r350568 QEMU riscv riscv64 1300038 1300038 root@callisto:/home/dclarke/foo # root@callisto:/home/dclarke/foo # cat pi.c /************************************************* * The Open Group Base Specifications Issue 6 * IEEE Std 1003.1, 2004 Edition *************************************************/ #define _XOPEN_SOURCE 600 #include <stdio.h> #include <stdlib.h> #include <math.h> int main ( int argc, char *argv[]) { long double pi128 = 3.1415926535897932384626433832795028841971693993751L; double pi64 = M_PI; printf (" the sizeof(pi128) is %i\n", sizeof(pi128) ); printf (" the sizeof(pi64) is %i\n", sizeof(pi64) ); printf ("pi128 may be %44.38Lg\n", pi128 ); printf ("pi64 may be %18.16g\n", pi64 ); return ( EXIT_SUCCESS ); } root@callisto:/home/dclarke/foo # root@callisto:/home/dclarke/foo # cat pi.s .file "pi.c" .option nopic .text .section .rodata .align 3 .LC2: .string " the sizeof(pi128) is %i\n" .align 3 .LC3: .string " the sizeof(pi64) is %i\n" .align 3 .LC4: .string "pi128 may be %44.38Lg\n" .align 3 .LC5: .string "pi64 may be %44.38Lg\n" .text .align 1 .globl main .type main, @function main: addi sp,sp,-64 sd ra,56(sp) sd s0,48(sp) addi s0,sp,64 mv a5,a0 sd a1,-64(s0) sw a5,-52(s0) lui a5,%hi(.LC0) ld a4,%lo(.LC0)(a5) sd a4,-32(s0) ld a5,%lo(.LC0+8)(a5) sd a5,-24(s0) lui a5,%hi(.LC1) fld fa5,%lo(.LC1)(a5) fsd fa5,-40(s0) li a1,16 lui a5,%hi(.LC2) addi a0,a5,%lo(.LC2) call printf li a1,8 lui a5,%hi(.LC3) addi a0,a5,%lo(.LC3) call printf ld a2,-32(s0) ld a3,-24(s0) lui a5,%hi(.LC4) addi a0,a5,%lo(.LC4) call printf ld a1,-40(s0) lui a5,%hi(.LC5) addi a0,a5,%lo(.LC5) call printf li a5,0 mv a0,a5 ld ra,56(sp) ld s0,48(sp) addi sp,sp,64 jr ra .size main, .-main .section .rodata .align 4 .LC0: .word 3306619320 .word 2221509004 .word 3041149649 .word 1073779231 .align 3 .LC1: .word 1413754136 .word 1074340347 .ident "GCC: (GNU) 8.2.0" root@callisto:/home/dclarke/foo # root@callisto:/home/dclarke/foo # root@callisto:/home/dclarke/foo # ./pi the sizeof(pi128) is 16 the sizeof(pi64) is 8 pi128 may be 2.0000076293945362811600603241536472604 pi64 may be 3.141592653589793 root@callisto:/home/dclarke/foo # root@callisto:/home/dclarke/foo # echo "16o 1074340347p 1413754136pq" | dc 400921FB 54442D18 root@callisto:/home/dclarke/foo # echo "16o 1073779231p 3041149649p 2221509004p 3306619320pq" | dc 4000921F B54442D1 8469898C C51701B8 root@callisto:/home/dclarke/foo # So that is perfect in memory and wrong in output. -- Dennis Clarke RISC-V/SPARC/PPC/ARM/CISC UNIX and Linux spoken GreyBeard and suspenders optional Yep, we have a problem in FreeBSD 13.0-CURRENT r355009 perhaps in _ldtoa.c still and I think maybe the real issue is with head/lib/libc/gdtoa/machdep_ldisQ.c where we see /* * Machine-dependent glue to integrate David Gay's gdtoa * package into libc for architectures where a long double * uses quad precision, such as sparc64. */ Well bingo ... other than the endianess we have the same data in memory and confusion in the printf output. So still digging. -- Dennis Clarke RISC-V/SPARC/PPC/ARM/CISC UNIX and Linux spoken GreyBeard and suspenders optional Hi Dennis, Thanks for the detailed report. I was finally able to find the source of the issue: GCC used to generate 64-bit long doubles and the workaround for that was still enabled. I've posted a patch addressing the issue here: https://reviews.freebsd.org/D25420 The test program gives the expected output for me, but if you'd like to verify the fix at well it would be helpful. A commit references this bug: Author: mhorne Date: Mon Jun 29 19:30:35 UTC 2020 New revision: 362788 URL: https://svnweb.freebsd.org/changeset/base/362788 Log: Fix printf(3) output of long doubles on RISC-V When the RISC-V port was initially committed to FreeBSD, GCC would generate 64-bit long doubles, and the definitions in _fpmath.h reflected that. This was changed to 128-bit in GCC later that year [1], but the definitions were never updated, despite the documented workaround. This causes printf(3) and friends to interpret only the low 64-bits of a long double in ldtoa, thereby printing incorrect values. Update the definitions now that both clang and GCC generate 128-bit long doubles. [1] https://github.com/riscv/riscv-gcc/commit/54b21fc5ae83cefec44bc2caed4a8c664c274ba0 PR: 242067 Reported by: Dennis Clarke <dclarke@blastwave.org> MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D25420 Changes: head/lib/libc/riscv/_fpmath.h A commit references this bug: Author: mhorne Date: Mon Jul 6 15:00:29 UTC 2020 New revision: 362964 URL: https://svnweb.freebsd.org/changeset/base/362964 Log: MFC r362788: Fix printf(3) output of long doubles on RISC-V PR: 242067 Changes: stable/12/lib/libc/riscv/_fpmath.h |