PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : gcc 4.1.1 -O3 Bug oder wieder mein Fehler?


SimonX
2007-01-08, 15:59:52
Hi

Hier der Code:

#include <stdlib.h>
#include <stdio.h>

static inline unsigned int _IdbmLocalGenSignature32(const void *s, int size)
{
unsigned int sig = 0x5A469df4 + size;
const unsigned int *d = s;

size >>= 2;
do {
sig += *d;
d++;
size--;
} while (size > 0);

return sig;
}

#define GenStructCrc_m(s) (_IdbmLocalGenSignature32(&(s)->p, sizeof((s)->p)))
#define SetStructCrc_m(s) ((s)->crc = GenStructCrc_m(s))

typedef struct
{
short pno;
short p_id;
} ProcId_t;

typedef struct
{
struct
{
ProcId_t id;
ProcId_t parent_id;
int parent_table;
int no_field;
int field_offset;
} p;
unsigned int crc;
} DefMan_t;



void DefCreate(DefMan_t *t, ProcId_t id, ProcId_t parent_id, int no_field)
{ int parent_table=parent_id.pno;
t->p.id=id;
t->p.parent_id=parent_id;
t->p.parent_table=parent_table;
t->p.no_field= no_field;
t->p.field_offset= 10;
SetStructCrc_m(t);

printf("%p %d %d\n", t, t->crc, GenStructCrc_m(t));


}

ProcId_t id={1,5};
ProcId_t parent_id={0,0};

void main()
{
DefMan_t *t=calloc(1, sizeof(DefMan_t));

DefCreate(t, id, parent_id, 13);

printf("%p %d %d\n", t, t->crc, GenStructCrc_m(t));
}

Und hier wie es übersetzt wurde:

>/usr/local/gcc-4.1.1/bin/gcc -v -fPIC -g3 -O3 -o x x.c
Using built-in specs.
Target: sparc-sun-solaris2.9
Configured with: ../gcc-4.1.1/configure --prefix=/usr/local/gcc-4.1.1 --disable-shared --disable-libgcj --disable-java-net
Thread model: posix
gcc version 4.1.1
/usr/local/gcc-4.1.1/libexec/gcc/sparc-sun-solaris2.9/4.1.1/cc1 -quiet -v -dD x.c -quiet -dumpbase x.c -mcpu=v7 -auxbase x -g3 -O3 -version -fPIC -o /var/tmp//cck6Y0dz.s
ignoring nonexistent directory "/usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/../../../../sparc-sun-solaris2.9/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/local/gcc-4.1.1/include
/usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/include
/usr/include
End of search list.
GNU C version 4.1.1 (sparc-sun-solaris2.9)
compiled by GNU C version 4.1.1.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: c25b3b01b3bd09a78990ec415a61556e
x.c: In function 'main':
x.c:61: warning: return type of 'main' is not 'int'
/usr/ccs/bin/as -V -Qy -s -K PIC -xarch=v8 -o /var/tmp//ccobkXha.o /var/tmp//cck6Y0dz.s
/usr/ccs/bin/as: Sun WorkShop 6 update 2 Compiler Common 6.2 Solaris_9_CBE 2001/04/02
/usr/local/gcc-4.1.1/libexec/gcc/sparc-sun-solaris2.9/4.1.1/collect2 -V -Y P,/usr/ccs/lib:/usr/lib -Qy -o x /usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/crt1.o /usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/crti.o /usr/ccs/lib/values-Xa.o /usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/crtbegin.o -L/usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1 -L/usr/ccs/lib -L/usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/../../.. /var/tmp//ccobkXha.o -lgcc -lc -lgcc -lc /usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/crtend.o /usr/local/gcc-4.1.1/lib/gcc/sparc-sun-solaris2.9/4.1.1/crtn.o
ld: Software Generation Utilities - Solaris Link Editors: 5.9-1.393
>x
20aa8 1514642975 1514642975
20aa8 1514642975 1514642980
>

Und auf einer Tru64 Alpha:

>gcc -v -g3 -O3 -o x x.c
>x
140004100 1514577439 1514577439
140004100 1514577439 1514905120
>

Das gleiche auf einer Linux Maschine:

> gcc -g3 -O3 -o x x.c
>x
0x501010 1514905119 1514905119
0x501010 1514905119 1514905120
>gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../gcc-4.1.1/configure --prefix=/local/Linux/local --disable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.1




Ist da was generell falsch?

Trap
2007-01-08, 16:13:30
Du liest Speicherinhalte die kein unsigned int sind mit einem unsigned int*, das ist undefiniertes Verhalten und damit dein Fehler.

Erlaubt ist typfremdes lesen ausschließlich mit einem char*.

SimonX
2007-01-08, 16:30:31
Das Problem tritt erst auf als ich die ProcId_t mit den beiden Shorts in die Struktur getan habe. Der Grund warum ich nicht mit char * arbeiten möchte ist das ich dadurch 4-mal mehr Add's bekomme als wenn ich mit einem 4-byte integer arbeite. Strukturen sind ja immer 4-byte aligned.

Der Compiler scheint DefMan_t noch nicht vollständig gescrhieben zu haben, wenn er den geinlined Code der Signature Funktion ausführt.

Die Signature Funktion wird vom Compiler vollständig aufgelöst und zwar ganz ohne Schleife, da ja die Structure-Size bekannt und konstant ist.

Wenn die Signature-Funktion nicht geinlined wird, dann geht alles. Der Compiler scheint zu vergessen, das die Signature Funktion einen Pointer auf die Struktur bekommen hat und somit sicher stellen muss, das die Struktur fertig ist.

Wenn ich DefMan_t an eine externe Dummy-Function übergebe, dann gehts wieder:

extern int FlushStruct(void *s);


#define GenStructCrc_m(s) (FlushStruct(&(s)->p), _IdbmLocalGenSignature32(&(s)->p, sizeof((s)->p)))
#define SetStructCrc_m(s) ((s)->crc = GenStructCrc_m(s))


Die Funktion ist dann in einem y.c implementiert und wird dazu gelinkt:
int FlushStruct(const void *s)
{
return 0;
}

Für mich sieht das eher nach einem Fehler des gcc bei -O3 aus. Bei -O2 geht es nämlich und auf der Sun auch bei -O3 wenn ich -fPIC weg lasse.

SimonX
2007-01-08, 16:57:15
Auf Assembler auf der Sun sieht man folgendes:

00000008 <DefCreate>:
8: 9d e3 bf 90 save %sp, -112, %sp
c: 88 10 00 19 mov %i1, %g4
10: c2 11 00 00 lduh [ %g4 ], %g1
14: c6 16 80 00 lduh [ %i2 ], %g3
18: c2 36 20 04 sth %g1, [ %i0 + 4 ]
1c: c2 16 a0 02 lduh [ %i2 + 2 ], %g1
20: fa 16 60 02 lduh [ %i1 + 2 ], %i5
24: c2 36 20 0a sth %g1, [ %i0 + 0xa ]
28: 03 16 91 a7 sethi %hi(0x5a469c00), %g1
2c: 82 10 62 08 or %g1, 0x208, %g1 ! 5a469e08 <main+0x5a469d6c>
30: 85 28 e0 10 sll %g3, 0x10, %g2
34: c6 36 20 08 sth %g3, [ %i0 + 8 ]
38: 85 38 a0 10 sra %g2, 0x10, %g2
3c: 86 10 20 0a mov 0xa, %g3
40: c4 26 00 00 st %g2, [ %i0 ]
44: 84 00 80 01 add %g2, %g1, %g2
48: c2 06 20 08 ld [ %i0 + 8 ], %g1
4c: 2f 00 00 00 sethi %hi(0), %l7
50: 7f ff ff ec call 0 <DefCreate-0x8>
54: ae 05 e0 00 add %l7, 0, %l7 ! 0 <DefCreate-0x8>
58: f6 26 20 0c st %i3, [ %i0 + 0xc ]
5c: c6 26 20 10 st %g3, [ %i0 + 0x10 ]
60: b2 10 00 18 mov %i0, %i1
64: c6 06 20 04 ld [ %i0 + 4 ], %g3
68: fa 36 20 06 sth %i5, [ %i0 + 6 ]
6c: 84 00 80 03 add %g2, %g3, %g2
70: 84 00 80 01 add %g2, %g1, %g2

Und hier ist der Fehler. g1 wurde bereits auf g2 (die "sig") addiert aber erst danach wird g1 richtig initalisiert. Es scheint nur zur hälfte initialisiert zu sein, da es wohl einen Teil der ProcId_t hält.

74: 03 00 00 00 sethi %hi(0), %g1
Hier wird g1 richtig gesetzt.


78: 84 00 80 1b add %g2, %i3, %g2
7c: 82 10 60 00 mov %g1, %g1
80: 84 00 a0 0a add %g2, 0xa, %g2
84: c4 26 20 14 st %g2, [ %i0 + 0x14 ]
88: b4 10 00 02 mov %g2, %i2
8c: f0 05 c0 01 ld [ %l7 + %g1 ], %i0
90: 40 00 00 00 call 90 <DefCreate+0x88>
94: 97 e8 00 02 restore %g0, %g2, %o3
98: 01 00 00 00 nop



Mit dem Flush Dummy sieht das so aus:


54: 90 10 00 18 mov %i0, %o0
58: c2 06 20 04 ld [ %i0 + 4 ], %g1
5c: c4 06 20 08 ld [ %i0 + 8 ], %g2
60: f4 06 00 00 ld [ %i0 ], %i2
64: c6 06 20 0c ld [ %i0 + 0xc ], %g3
68: c8 06 20 10 ld [ %i0 + 0x10 ], %g4
6c: 90 10 00 18 mov %i0, %o0
70: 21 16 91 a7 sethi %hi(0x5a469c00), %l0
74: a0 14 22 08 or %l0, 0x208, %l0 ! 5a469e08 <main+0x5a469d34>
78: b4 06 80 10 add %i2, %l0, %i2
7c: b4 06 80 01 add %i2, %g1, %i2
80: b4 06 80 02 add %i2, %g2, %i2
84: b4 06 80 03 add %i2, %g3, %i2
88: b4 06 80 04 add %i2, %g4, %i2

Da holt er sich alles wieder aus dem Speicher.

Vorher wollte er wohl direkt Register benutzen, nur das er die Register verwendet bevor sie fertig sind.

Trap
2007-01-08, 17:24:22
Für mich sieht das eher nach einem Fehler des gcc bei -O3 aus. Bei -O2 geht es nämlich und auf der Sun auch bei -O3 wenn ich -fPIC weg lasse.
Dein Code hat undefiniertes Verhalten, damit kann das kein Bug im Compiler sein, es gibt kein richtiges Ergebnis, also ist das was der GCC erzeugt nicht falsch.

Probier mal -fno-strict-aliasing, das schaltet ab, dass GCC davon ausgeht, dass man Pointer nur typrichtig benutzt.

SimonX
2007-01-08, 17:46:36
Ja, mit "-fno-strict-aliasing" gehts.

Aber eigentlich ist ja die Signature-Funktion eine eigene Funktion die gemäss der Funktionsdefinition einen memory-pointer bekommt. Somit drüfte dort sowieso kein Aliasing passieren.

Alle Beispiele mit ANSI Aliasing beziehen sich nur auf Union's. Hier wird keine Union benutzt. Auch ist es normal unter C, Strukturen als void * oder const void * zu übergeben. Durch das starke Inlining hier nutzt der Compiler aliasing obwohl er es garnicht machen dürfte.

Trap
2007-01-09, 00:28:00
Dein Code hat undefiniertes Verhalten. Genau zu ergründen wie GCC was anderes produziert als du erwartest nützt niemand was.

So wie C definiert ist, ist es als hätte jeder Typ einen eigenen Speicher und nur mit char* kann man auf alle Speicher zugreifen (ausführlicher gibt es das z.B. unter http://www.cellperformance.com/mike_acton/2006/06/understanding_strict_aliasing.html).

Für deine Crc-Funktion ist es aus Performancegründen sinnvoll auch auf typfremde Daten über den Pointer zugreifen zu können, die Unterstützung dafür muss man bei GCC mit -fno-strict-aliasing anschalten, sonst kommt kein vorhersagbares Verhalten raus.
Deine Funktion macht übrigens unter anderem für structs von Größe 1 Unsinn.

Strukturen sind ja immer 4-byte aligned.
Wo steht das?

SimonX
2007-01-09, 19:06:29
Danke für die Hilfe!

Wie ein deinem Link geschrieben ist das alles ein Tradeoff und Black-Art.

Meine Lösung um auch ohne "-fno-strict-aliasing" zu leben ist:

extern int FlushStruct(void *s);
#define SetStructCrc_m(s) (FlushStruct(s), (s)->crc = GenStructCrc_m(s))

Und dann in einem anderen .o file
int FlushStruct(void *s)
{
return 0;
}
so das der gcc nicht wissen kann wie die Struktur modifiziert wurde und somit direkt vom Speicher lesen muss. Das wird dann immer und bei jedem Compiler funktionieren.

Das Crc Konstrukt wird von mir genutzt, um schnell shared-memory Strukturen zu validieren. Jemand könnte sie ja kaput gemacht haben. Die werden nur selten verändert aber häufig gelesen.

Zu den "4-byte aligned": Alle aktuellen Platformen, auf den ich arbeite, schrieben das so in ihrer ABI vor.

Trap
2007-01-09, 20:53:07
Meine Lösung um auch ohne "-fno-strict-aliasing" zu leben ist:

so das der gcc nicht wissen kann wie die Struktur modifiziert wurde und somit direkt vom Speicher lesen muss. Das wird dann immer und bei jedem Compiler funktionieren.
Halte ich nicht für eine saubere Lösung, einerseits kostet das Performance und andererseits ist das soweit ich weiß immernoch undefiniert. whole-program-optimization wird in immer mehr Compiler eingebaut.

Zu den "4-byte aligned": Alle aktuellen Platformen, auf den ich arbeite, schrieben das so in ihrer ABI vor.
Das geht aber nur wenn alle structs auf ein Vielfaches von 4 Byte gepadded werden, sonst sind die alignments in arrays falsch. Ist das auf deinen Plattformen wirklich so?

SimonX
2007-01-11, 12:44:55
whole-program-optimization wird in immer mehr Compiler eingebaut.
Ist in einer Shared-Library, da ist dann wirklich keine cross-Optimierung mehr möglich. Diese o-files werden separat übersetzt.



Das geht aber nur wenn alle structs auf ein Vielfaches von 4 Byte gepadded werden, sonst sind die alignments in arrays falsch. Ist das auf deinen Plattformen wirklich so?
Alle mir bekannten Platformen haben mindestens 4byte-alignment und ich kenne die Strukturen, bei denen ich den CRC brauche. Beim gcc muss man sowas erst durch -fpacked (oder so) disablen.