GCC 2.7.2.2/2.8.0 Bugs

This page contains a list of bugs in gcc 2.7.2.2 and 2.8.0 that can affect the djgpp programs. All the bugs were tested by me with djgpp v2.01 so exists.

Even when these bugs can affect djgpp programs nobody found the bugs using djgpp, that's because some of them are very hard to hit. The number 10 was reported to the djgpp list.

Most of them are fixed in 2.8.0 so consider the upgrade.

* -funroll-loops generates bad code in do-while statements using wrap-around exit conditions Ok 2.8.0
* The scheduler for assigments of the compiler fails using -O under some complex situations (3 examples) Ok 2.8.0
* The extentions to initialize structures naming the members fails Ok 2.8.0
* The C++ front end compares (void *)array and (void *)&array as different values even when they are the same Ok 2.8.0
* GCC fails to generate code for some struct definitions Ok 2.8.0
* The && extention produces a silly error on some C++ code Ok 2.8.0
* GCC generates code that can change your results just adding a line when optimizing Not considered a bug by FSF
* When moving arithmetic outside loops GCC sometimes fails Ok 2.8.0
* -O2 removes important boolean operations in some cases Ok 2.8.0
* Internal error when parsing some C++ code In 2.8.0
* Parameters passed as registers fails (regparm(3)) In 2.8.0/1
* GCC reports wrong line numbers when you use multiline macros with strings. In 2.8.0/1


Killed in 2.8.0

Description: -funroll-loops generates bad code in do-while statements using wrap-around exit conditions.

Example to test the bug:

/* Compile with -O2 -funroll-loops * */ unsigned char S[256]; int main(int argc, char **argv) { unsigned char i = 0; do S[i] = i; while (++i); return i; } /**** End of example *****/ Compile with: -O2 -funroll-loops

Effect: The loop will never end (press ^Break to stop it), compiling with only -O2 works OK.

Detailed explanation: The code generated by GCC for this loop is:

.file "pp.c" gcc2_compiled.: ___gnu_compiled_c: .text .align 2 .globl _main _main: pushl %ebp movl %esp,%ebp call ___main movb $0,_S <======== That's an optimization for the S[0]=0 movb $1,%cl <======== CL starts with 1 and is the counter testb %cl,%cl <======== This line an the 2 next lines are a redundant je L6 code. Shows a fail in the optimizer but not .align 2,0x90 serious. L2: movzbl %cl,%eax <======= That's the unrolled loop :-P movb %cl,_S(%eax) movb %cl,%dl incb %dl movzbl %dl,%eax movb %dl,_S(%eax) movb %cl,%dl addb $2,%dl movzbl %dl,%eax movb %dl,_S(%eax) movb %cl,%dl addb $3,%dl movzbl %dl,%eax movb %dl,_S(%eax) addb $4,%cl <***BUG*** Here CL have 4*n added so NEVER will be 0! because CL started with 1. jne L2 <========= So here we have an infinite loop. L6: movzbl %cl,%eax leave ret .comm _S,256 Reported to bug-gcc by: Niels Müller

Analized by SET.

GCC 2.8.0 Test:

.file "p1.c" gcc2_compiled.: ___gnu_compiled_c: .text .p2align 2 .globl _main _main: pushl %ebp movl %esp,%ebp pushl %ebx xorl %ecx,%ecx <=== Starts with 0 movl $_S,%ebx <=== No S[0] special .align 2,0x90 L2: movzbl %cl,%eax movb %cl,(%eax,%ebx) movb %cl,%dl incb %dl movzbl %dl,%eax movb %dl,(%eax,%ebx) movb %cl,%dl addb $2,%dl movzbl %dl,%eax movb %dl,(%eax,%ebx) movb %cl,%dl addb $3,%dl movzbl %dl,%eax movb %dl,(%eax,%ebx) addb $4,%cl jne L2 xorl %eax,%eax movl -4(%ebp),%ebx leave ret .comm _S,256


Killed in 2.8.0

Description: The scheduler for assigments of the compiler fails using -O under some complex situations.

Example 1 to test the bug:

/* bugs/gcc.2722.386.c * This program triggers a code generation bug in gcc 2.7.2.2 for * Intel 386 (and later) processors. * The bug has been present at least since gcc 2.6.3. * * When compiled correctly, the program should produce no output. * Any output on the form "X is Y, should be Z" indicates an error. * On the Intel 386, the erroneous output is: * * SP[3] is 0x0000000B, should be 0x00000061 * * In the `update()' function below, gcc schedules the load of A1 at 2a * and store at 2b BEFORE the load of SP1 at 1a and store at 1b. * The store at 2b CLOBBERS the original value of SP1, causing the * load at 1a to pick up the wrong value, and consequently store * the wrong value at 1b. */ #include <stdio.h> struct state { unsigned *SP; unsigned ARG0; unsigned ARG1; unsigned STACK[100]; }; void update(struct state *state) { unsigned *SP = state->SP; unsigned SP1 = SP[1]; /*1a*/ unsigned SP2 = SP[2]; unsigned SP3 = SP[3]; unsigned A0 = state->ARG0; unsigned A1 = state->ARG1; /*2a*/ SP[3] = SP1; /*1b*/ SP[2] = SP3; SP[1] = A1; /*2b*/ SP[0] = SP2; state->ARG1 = A0; state->ARG0 = SP3; } void check(const char *name, unsigned now, unsigned expected) { if( now != expected ) fprintf(stderr, "%s is 0x%08X, should be 0x%08X\n", name, now, expected); } int main(void) { struct state state; state.STACK[99] = 99; state.STACK[98] = 98; state.STACK[97] = 97; state.STACK[96] = 96; state.SP = &state.STACK[96]; state.ARG1 = 11; state.ARG0 = 10; update(&state); check("SP[3]", state.SP[3], 97); check("SP[2]", state.SP[2], 99); check("SP[1]", state.SP[1], 11); check("SP[0]", state.SP[0], 98); check("ARG1", state.ARG1, 10); check("ARG0", state.ARG0, 99); return 0; } /**** End of example *****/ Compile with: -O

Effect: GCC assigns a wrong value to SP[3] because he destroyed the right value when reordering the instructions. See comments in the source.

To test it better you can use a debugger and you'll see how gcc reorders the assigments and fails.

There are an unofitial patch for it.

Reported to bug-gcc by: Mikael Pettersson.

Example 2 to test the bug:

typedef struct _foo { unsigned long src1; unsigned long src2; unsigned long src3; unsigned long result; } foo, *pfoo; bugbug(pfoo this) { unsigned long src2 = this->src2; unsigned long result; unsigned short *merge; unsigned short tmp; src2 = (src2 & 2) >> 1; result = this->src3; merge = (unsigned short *)&result; tmp = (unsigned short)this->src1; merge[src2] = tmp; this->result = result; return 1; } main() { foo this; this.src1 = 0x55555555; this.src2 = 2; this.src3 = 0xaaaaaaaa; this.result = 0; bugbug(&this); printf("result is %x\n", this.result); } Compile with: -O

Effect: GCC assigns a wrong value to this.result. Compiling the code without -O the program outputs: 5555aaaa but with -O aaaaaaaa.

Detailed explanation: The code generated by GCC is:

.file "t.c" gcc2_compiled.: ___gnu_compiled_c: .text .align 2 .globl _bugbug _bugbug: pushl %ebp movl %esp,%ebp subl $4,%esp pushl %esi pushl %ebx movl 8(%ebp),%ecx ; Pointer to the structure is ECX movl 4(%ecx),%eax ; EAX(src2)=this->src2 andl $2,%eax shrl $1,%eax ; EAX(src2)=(src2 & 2) >> 1; movl 8(%ecx),%esi ; ESI(result)=this->src3 movl %esi,-4(%ebp) ; -4(EBP)(result)=ESI leal -4(%ebp),%ebx ; EBX(merge)=(unsigned short *)&result; movw (%ecx),%dx ; DX(tmp)=this->src1 movw %dx,(%ebx,%eax,2) ; EBX[EAX*2](merge[src2])=tmp; ; The line above invalidates ESI because is touching merge and hence is ; touching -4(EBP) and hence result. So now -4(EBP) and ESI doesn't match movl %esi,12(%ecx) ; BUG! this->result = result; but with ESI (no ; longer valid) so this->result=this->src3! movl $1,%eax ; return 1; leal -12(%ebp),%esp popl %ebx popl %esi leave ret I don't know if that's produced by the same bug in GCC.

Reported to bug-gcc by: Wim Colgate.

Analized by SET.

Example 3 to test the bug:

int main (){ int i,n,f=0,how_many_spokes=3; int fac[50][3]; double bug_fix=0; n=9; f=0; /* this loop yields the correct result */ for(i=0;i<n-1;i++){ fac[f][0]=n+i; fac[f][1]=n+i+1; fac[f++][2]=i+(how_many_spokes+1)*n; /* accessing f in the middle fixes the optimization bug */ bug_fix+=f; fac[f][0]=n+i+1; fac[f][1]=i+(how_many_spokes+1)*n+1; fac[f++][2]=i+(how_many_spokes+1)*n; } for(i=0;i<f;i++){ printf("%d\t%d\t%d\n",fac[i][0],fac[i][1],fac[i][2]); } printf("\n"); f=0; /* this loop yields the wrong result */ for(i=0;i<n-1;i++){ fac[f][0]=n+i; fac[f][1]=n+i+1; fac[f++][2]=i+(how_many_spokes+1)*n; fac[f][0]=n+i+1; fac[f][1]=i+(how_many_spokes+1)*n+1; fac[f++][2]=i+(how_many_spokes+1)*n; } for(i=0;i<f;i++){ printf("%d\t%d\t%d\n",fac[i][0],fac[i][1],fac[i][2]); } return(0); } Compile with: -O -funroll-loops

Effect: The first loop produces the right result just because the line bug_fix+=f; that forces to avoid the destruction of f. This time appears because of the -funroll-loops interference.

Reported to bug-gcc by: Uwe F. Mayer.


Killed in 2.8.0

Description: The extentions to initialize structures naming the members fails.

Example to test the bug:

struct s1 { int a; }; struct s2 { int i; struct s1 s; }; struct s2 s2 = { /* .i = 1, */ .s = { 2 } }; Effect: This code must initialize the s2 variable with 2 the s member, but it initialize both members to 0.

Detailed explanation:

GCC fails in the initialization:

.file "p1.c" gcc2_compiled.: ___gnu_compiled_c: .globl _s2 .data .align 2 _s2: .space 4 .space 4 <======= An extra initialization! .long 2 Uncommenting .i=1 it works OK and generates .long 1 .long 2.

Reported to bug-gcc by: Christof Petig.

GCC 2.8.0 Test:

.file "p1.c" gcc2_compiled.: ___gnu_compiled_c: .globl _s2 .data .p2align 2 _s2: .space 4 .long 2


Killed in 2.8.0

Description: The C++ front end compares (void *)array and (void *)&array as different values even when they are the same.

Example to test the bug:

#include <stdio.h> int aa[3]={7,8,9}; int main() { printf("(void*) aa is %X and (void*) &aa is %X so they are %s.\n",(void*) aa, (void*) &aa,((void*) aa != (void*) &aa) ? "not equal!?!" : "equal"); return 0; } Detailed explanation:

If you compile it in C you'll get the right answer, both values are equal so the ? : works OK. But if you compile it in C++ you'll get too equal values but the ? : says they are different.

Reported to bug-gcc by: Daniel W. Moore.

Adapted to be more explanatory by SET.


Killed in 2.8.0

Description: GCC fails to generate code for some struct definitions.

Example to test the bug:

typedef struct { int number_of_lines; long long int * coefficients; } * Pmatrix; void matrix_add( Pmatrix a, Pmatrix b, Pmatrix c) { a->coefficients[a->number_of_lines] = b->coefficients[b->number_of_lines] + c->coefficients[c->number_of_lines]; } That's a very strange bug because gcc refuses to generate de code reporting:

tmp.c: In function `matrix_add': tmp.c:14: fixed or forbidden register was spilled. This may be due to a compiler bug or to impossible asm statements or clauses. Reported to bug-gcc by: Fabien COELHO.


Killed in 2.8.0

Description: The && extention produces a silly error on some C++ code.

Example to test the bug:

int main() { static void *labelptr = &&foo; static unsigned array2[3] = { 1, 2, 3 }; foo: return(0); } Detailed explanation:

The && extention allows to GCC programs know the address of a C label. If the example is compiled in C it works fine, but in C++ reports:

tmp.C: In function `int main()': tmp.C:10: jump to label `foo' tmp.C:8: crosses initialization of `unsigned int array2[3]' When that isn't true because there is no jump there just a reference. I guess it can be reported as a warning when -Wall is enabled, but never as an error.

Reported to bug-gcc by: Kevin P Lawton.


Not considered as a bug by the FSF, use -ffloat-store

Description: GCC generates code that can change your results just adding a line when optimizing.

Example to test the bug:

#include <stdio.h> int main() { auto double x, oldx, y; y = 1.0; x = 1.0 + y; oldx = 1.0; do { y /= 2.0; oldx = x; x = 1.0 + y; } while ( x < oldx ); printf( "%e\n", y ); /* if ( oldx > 1.0 ) printf( "!\n" ); */ return 0; } Compile with: -O

Effect: Compiling with -O the returned value is different that without -O, uncommenting the code the result is like without -O.

Detailed explanation:

That's a result of what optimizations are turned on with -O, one of them is -fno-float-store, this switch produces code that process values in the internal 80 bits FPU format, adding more resolution. The problem here is that some code that depends on the resolution will be affected and behave different when -Ox is enabled. From my point of view (the same point of view of the reporter) this switch must not be on with -O. BTW -ffast-math isn't on because of similar problems.

Reported to bug-gcc by: Mans Karlberg.


Killed in 2.8.0

Description: When moving arithmetic outside loops GCC sometimes fails

Example to test the bug:

#include <stdio.h> void main (void) { int i, j, t, x; t = x = 0; for (i=0; i<2; i++) { for (j=0; j<10; j++) { t++; x = 17 * t; } printf("t = %d x = %d\n", t, x); } printf("\n"); } Compile with: -O2

Effect: Compiling with -O2 the output is wrong, the program reports x=340 and x=510 instead of 170 and 340.

Detailed explanation:

This bug is produced when GCC moves the multiplication outside the loop, it makes 170+17*t instead of 17*t. I guess is trying to make 17*t for one part and 170*(i+1) by other and when combinating the both things interfers.

.file "p.c" gcc2_compiled.: ___gnu_compiled_c: .text LC0: .ascii "t = %d x = %d\12\0" LC1: .ascii "\12\0" .align 2 .globl _main _main: pushl %ebp movl %esp,%ebp pushl %esi pushl %ebx call ___main xorl %ebx,%ebx ; t=0 xorl %esi,%esi ; i=0 .align 2,0x90 L5: movl $9,%eax ; Counter 9 times .align 2,0x90 L9: incl %ebx ; decl %eax ; That's the for not very well optimized jns L9 ; ; Now t(EBX) is 10 ; That's the buggy code: movl %ebx,%eax sall $4,%eax leal 170(%ebx,%eax),%eax ; 170+t*17 !!!!!! and not t*17 !?!?!? pushl %eax pushl %ebx pushl $LC0 call _printf addl $12,%esp incl %esi ; i++ cmpl $1,%esi jle L5 ; next pushl $LC1 call _printf leal -8(%ebp),%esp popl %ebx popl %esi leave ret Reported to bug-gcc by: Viyukova Nadezhda Ivanovna. For Solaris 2.5.1

Analized by SET.


Killed in 2.8.0

Description: -O2 removes important boolean operations in some cases

Example to test the bug:

#include <stdio.h> typedef unsigned long ulong; typedef unsigned int uint; extern ulong test_func(uint const_tables); int main() { ulong ret; if ((ret=test_func(0)) != (ulong) ~0L) { printf("test_func returned wrong value: %lx, should have been ffffffff\n", ret); exit(1); } printf("test ok\n"); exit(0); return 0; } ulong other_tables; ulong test_func(uint const_tables) { ulong not_const_tables; not_const_tables= ~(((ulong) 1L << const_tables) - 1L); other_tables= not_const_tables ^ ((ulong) 1L << const_tables); return not_const_tables; } Compile with: -O2

Effect: Compiling with -O2 the value returned by test_func is very wrong, in fact the not operation is removed.

Detailed explanation:

This bug is produced because GCC tries to reduce the number of operations for the other_tables calculation (in fact GCC reduces it to 0) but in the operation the not is lost.

.globl _test_func _test_func: pushl %ebp movl %esp,%ebp movl 8(%ebp),%ecx movl $1,%eax sall %cl,%eax ; NO more operations!!! movl $0,_other_tables leave ret .comm _other_tables,4 Reported to bug-gcc by: Monty. The bug doesn't appear under Solaris.

Analized by SET.


Introduced in 2.8.0

Description: Parsing a member data pointer to a member function can produce an internal error

Example to test the bug:

class c { void (c::*x)(); public: void f() { this->x(); } }; Effect: It generates an internal error in 2.8.0 under DOS and the same happends with egcs 1.0.1 under Linux.

p1.cc: In method `void c::f()': p1.cc:4: Internal compiler error. p1.cc:4: Please submit a full bug report to `bug-g++@prep.ai.mit.edu'. Reported to djgpp-list by: Ruiter de M.

Introduced in 2.8.0 is in 2.8.1 too

Description: When passign parameters as registers and calling functions through pointers gcc clobbers one of the parameters.

Example to test the bug:

#include <stdlib.h> #define ATTR __attribute__((__regparm__(3))) struct big { int a[32]; }; static void ATTR fun1 (struct big y) { } static struct big ATTR fun2 () { static struct big r; return r; } static void ATTR fun3 (int a, int b, int c, int d, int e) { if (a != 17 || b != 18 || c != 19 || d != 20 || e != 21) abort (); } static void test1 (void (*f1)(struct big) ATTR, struct big (*f2)(void) ATTR, void (*f3)(int, int, int, int, int) ATTR) { struct big b; f2 (); f1 (b); f3 (17, 18, 19, 20, 21); } int main (void) { test1 (fun1, fun2, fun3); return 0; } Compile with: -O

Detailed explanation:

Looks like gcc choose the wrong register to make the call through a pointer, here is the assembler produced:

test1: pushl %ebp movl %esp,%ebp subl $256,%esp pushl %edi pushl %esi pushl %ebx movl 8(%ebp),%ebx movl 12(%ebp),%edx leal -256(%ebp),%eax call *%edx addl $-128,%esp movl %esp,%edi leal -128(%ebp),%esi cld movl $32,%ecx rep movsl call *%ebx subl $-128,%esp pushl $21 pushl $20 movl $19,%ecx <--- %ecx used for argument movl $18,%edx movl $17,%eax movl 16(%ebp),%ecx <--- %ecx used for pointer call *%ecx leal -268(%ebp),%esp popl %ebx popl %esi popl %edi movl %ebp,%esp popl %ebp ret Reported to bug-gcc by: An annonymous Linux user.

Introduced in 2.8.0 is in 2.8.1 too

Description: GCC changed the way to preprocess multiline strings, it produces wrong line number reports when you use multiline strings in a macro. Additionally it breaks some assembler programs.

Example to test the bug:

#define FOO "this \ is \ three \ lines" int line5 = __LINE__; char foo[] = FOO; int line7 = __LINE__; This is a syntax error on line 8. Effect: GCC will report parse error in line 11 instead of 8. Detailed explanation:

The strings aren't preprocessed letting this job to the compiler, here is the output produced by the preprocessor (gcc -V 2.8.1 -E /tmp/f.c):

# 1 "/tmp/f.c" int line5 = 5; char foo[] = "this \ is \ three \ lines" ; int line7 = 7; This is a syntax error on line 8. Reported to bug-gcc by: Peter Jeremy.


This page hosted by Get your own Free Home Page


1