Date: Sun, 14 Jul 2002 18:40:04 -0700 (PDT) From: Dan Lukes <dan@obluda.cz> To: freebsd-bugs@FreeBSD.org Subject: Re: bin/40209: __dtoa broken with -O2 or -O3 optimisation Message-ID: <200207150140.g6F1e4SI056496@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/40209; it has been noted by GNATS.
From: Dan Lukes <dan@obluda.cz>
To: FreeBSD-gnats-submit@FreeBSD.ORG
Cc: Bruce Evans <bde@zeta.org.au>
Subject: Re: bin/40209: __dtoa broken with -O2 or -O3 optimisation
Date: Mon, 15 Jul 2002 03:31:55 +0200
Bruce Evans wrote:
> Can you do some more work to isolate the bug? I haven't had time.
Well, I did the very time-expensive depth-in analysis of the code. It
still seems to be bug in optimisation code of GCC.
I used the copy of strtod.c source to create small test program, compiled
it with gcc then disassembled by IDA (because I discovered that the gdb
has sometimes incorrect assumptions about variable locations, and - we
can't trust the gdb if we are searching gcc bugs) and checked it for
functional equivalence with source C code.
The machine code didn't what it should do. Relevant parts of C source
code (with line numbers):
226: #define word0(x) ((ULong *)&x)[1]
227: #define word1(x) ((ULong *)&x)[0]
--- skipped ---
1933: d2 = d;
1934: word0(d2) &= Frac_mask1;
1935: word0(d2) |= Exp_11;
--- text on lines 1936-1968 isn't executable code ---
1969: denorm = 0;
Related machine code (with disassembler and my comments;
disassembly-flavor set to 'intel', focus your eyes for comments starting
with '#### !!!?'):
; +-------------------------------------------------------------------+
; | This file is generated by The Interactive Disassembler (IDA) |
; | Copyright (c) 2001 by DataRescue sa/nv, <ida@datarescue.com> |
; | Licensed to: Dan Lukes, 1 user upg, 03/2002 |
; +-------------------------------------------------------------------+
; File Name : a.out
; Format : ELF (Executable)
; Source File : 'crtstuff.c'
; Source File : 'x.c'
; Source File : 'MYstrtod.c'
; NOTE: MYstrtod.c is exact copy od strtod,
; the rename of __dtoa to MY__dtoa is the only modification
model flat
; ========== [removed] ===========
; Segment type: Pure code
; Segment permissions: Read/Execute
_text
segment dword public 'CODE' use32
assume cs:_text, ds:_data
;org 80480B8h
; ========== [removed] ===========
; ------------- S U B R O U T I N E --------------------------------
; Attributes: bp-based frame
; char *MY__dtoa(double d,int mode,int ndigits,int *decpt,int *sign,
; char **rve,char **resultp)
public MY__dtoa
MY__dtoa
proc near ; CODE XREF: main+52 p
; local variables
i
= dword ptr -9Ch
var_94
= dword ptr -94h
j
= dword ptr -90h
s0
= dword ptr -8Ch
s
= dword ptr -88h
S
= dword ptr -84h
mhi
= dword ptr -80h
mlo
= dword ptr -7Ch
b
= dword ptr -78h
denorm
= dword ptr -74h
spec_case
= dword ptr -70h
s5
= dword ptr -6Ch
m5
= dword ptr -68h
m2
= dword ptr -64h
leftright
= dword ptr -60h
k_check
= dword ptr -5Ch
k0
= dword ptr -58h
k
= dword ptr -54h
ilim1
= dword ptr -50h
ilim0
= dword ptr -4Ch
ilim
= dword ptr -48h
dig
= dword ptr -44h
b5
= dword ptr -40h
b2
= dword ptr -3Ch
L
= dword ptr -38h
ds_
= qword ptr -34h
eps
= dword ptr -2Ch
d2
= qword ptr -28h
be
= dword ptr -20h
bbits
= dword ptr -1Ch
local_d
= qword ptr -18h
var_C
= byte ptr -0Ch
; subroutine parameters
d
= qword ptr 8
mode
= dword ptr 10h
ndigits
= dword ptr 14h
decpt
= dword ptr 18h
sign
= dword ptr 1Ch
rve
= dword ptr 20h
resultp
= dword ptr 24h
; ========== [removed] ===========
;C:1929: if ( (i = (int)((word0(d) >> Exp_shift1) &
(Exp_mask>>Exp_shift1))) ) {
mov edx, dword ptr [ebp+local_d+4]
mov ecx, edx
shr ecx, 14h ; Exp_shift1
mov esi, ecx
add esp, 10h ; Add
and esi, 7FFh ; Exp_mask>>Exp_shift1
mov [ebp+b], eax
jz loc_804A31C ; Jump if Zero (ZF=1)
;C:1933: d2 = d;
; #### !!!? following two instructions move only bottom half of d to d2
mov eax, dword ptr [ebp+local_d]
mov dword ptr [ebp+d2], eax
;C:1934: word0(d2) &= Frac_mask1;
; #### !!!? upper half of d2 is still not copied from d, so it is used
uninitialised
mov eax, dword ptr [ebp+d2+4]
and eax, 0FFFFFh ; Frac_mask1
mov edi, dword ptr [ebp+local_d+4]
;C:1935: word0(d2) |= Exp_11;
or eax, 3FF00000h ; Exp_11
;C:1963: i -=Bias;
sub esi, 1023 ; Bias
mov ebx, [ebp+bbits]
; #### !!!? there is completed the copy of upper half of d to d2
; #### !!!? (via edi register),
; #### !!!? but it is overwritten by eax computed
; #### !!!? from uninitialized upper half of d2
mov dword ptr [ebp+d2+4], edi
mov dword ptr [ebp+d2+4], eax
;C:1969: denorm = 0;
mov [ebp+denorm], 0
; ========== [removed] ===========
===================================================================
The code on line 1933 is improperly compiled because the compiler
ignored dependencies on lines 1934 and 1935.
If you start the bad optimized code under gcc with breakpoint on line
1933 and you manually set d2 ('set d2=d') whenever the code stops, the
program start to work O.K.
If somebody is interested in complete disassembly of compiled __dtoa
with comments containing original source C code, lets me know.
The minimal testing environment can be created using following patch source:
*** /dev/null Mon Jul 15 01:54:32 2002
--- Makefile Mon Jul 8 18:14:01 2002
***************
*** 0 ****
--- 1,23 ----
+ #OPTFLAGS=-fno-schedule-insns2
+ #OPTFLAGS=-fno-strict-aliasing
+ CFLAGS=-g -Wall -pipe -O3 $(OPTFLAGS)
+
+ ALL: a.out
+
+ MYstrtod.c: /usr/src/lib/libc/stdlib/strtod.c
+ sed -e 's/__dtoa/MY__dtoa/g' /usr/src/lib/libc/stdlib/strtod.c > MYstrtod.c
+
+ MYstrtod.o: MYstrtod.c Makefile
+ $(CC) $(CFLAGS) -I/usr/src/include -c MYstrtod.c -o MYstrtod.o
+
+ x.o: x.c Makefile
+ $(CC) $(CFLAGS) -c x.c -o x.o
+
+ a.out: x.o MYstrtod.o Makefile
+ $(CC) $(CFLAGS) -static x.o MYstrtod.o
+
+ clean:
+ -rm x.o a.out MYstrtod.o MYstrtod.c
+
+ run: a.out
+ -./a.out
*** /dev/null Mon Jul 15 01:54:32 2002
--- x.c Tue Jul 9 01:00:23 2002
***************
*** 0 ****
--- 1,21 ----
+ #include <stdio.h>
+
+ #define mode 3
+ #define ndigits 6
+
+ char *MY__dtoa(double, int, int, int *, int *, char **, char **);
+
+ int main(argc, argv) {
+ int i, dsgn;
+ int xdecpt=1;
+ char *xdtoaresult, *xresult, *rve;
+
+ for (i=11;i<14;i++) {
+ xdecpt=1; xdtoaresult = xresult = rve = NULL;
+ xresult = MY__dtoa((double)i, mode, ndigits, &xdecpt, &dsgn, &rve,
&xdtoaresult);
+ xdtoaresult[rve - xresult]='\0';
+ puts(xdtoaresult);
+ }
+
+ return(0);
+ }
Please note the only first round (i=11) give improper results as the
memory place where d2 lives is already initialised from previous run
(e.g. i=12 and i=13 return expected values).
Disabling schedule-insns2 OR strict-aliasing during compilation create
code that seems to work (but it msy contain other undiscovered bugs).
==================================
During depth-in analysis of the strtod.c code I discovered it's dirty
code, somewhat. It never test's if malloc has been succesfull - it just
reference it without checking. Look into Balloc function and __dtoa line
2042. IMHO, good code should never abends unless programmer decide to
abend it, so current beta-quality strtod.c code is acceptable in
current, but it's not suitable for stable.
Should I submit separate PR about it ? (I see no simple fix without
rewriting large amount of code as affected functions has no concept of
"internal error/resource unavaiable" reporting and/or handling).
Dan
Note: english isn't my native language
--
Dan Lukes tel: +420 2 21914205, fax: +420 2 21914206
root of FIONet, KolejNET, webmaster of www.freebsd.cz
AKA: dan@obluda.cz, dan@freebsd.cz,dan@kolej.mff.cuni.cz
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200207150140.g6F1e4SI056496>
