Bug 34300

Summary: gcc 4.2.2 miscompiles code that uses global register variables
Product: gcc Reporter: bruno
Component: middle-endAssignee: Not yet assigned to anyone <unassigned>
Status: UNCONFIRMED ---    
Severity: normal CC: gcc-bugs, hjl.tools, ismail, nospam, sds
Priority: P3    
Version: 4.2.2   
Target Milestone: ---   
Host: i686-pc-linux-gnu Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu Known to work: 4.1.2, 4.3.0
Known to fail: 4.2.2 Last reconfirmed:
Attachments: source code exhibiting the bug

Description bruno 2007-11-30 10:45:04 UTC
$ gcc -v
Using built-in specs.
Target: i686-pc-linux-gnu
Configured with: ../gcc-4.2.2/configure --prefix=/packages/gnu-inst-gcc/4.2.2 --enable-shared --enable-version-specific-runtime-libs --enable-nls --enable-threads=posix --enable-__cxa_atexit --with-as=/packages/gnu/bin/as --with-ld=/packages/gnu/bin/ld --with-gmp=/packages/gnu-inst-gcc/4.2.2 --with-mpfr=/packages/gnu-inst-gcc/4.2.2 --with-ecj-jar=/gfs/ibook/Volumes/ExtData/source/gnu/gcc/sourceware.org-ecj/ecj-latest.jar
Thread model: posix
gcc version 4.2.2
$ gcc bug.c
$ ./a.out ; echo $?
0
$ gcc -O bug.c
$ ./a.out ; echo $?
Aborted
134

================================= bug.c ====================================
typedef unsigned char UBYTE;
typedef long SLONG;
typedef unsigned long ULONG;
typedef ULONG uint32;
typedef SLONG sint32;
typedef sint32 sintL;
typedef uint32 uintL;
typedef sint32 sintP;
typedef uint32 uintP;
typedef void * gcv_object_t;
typedef uintP oint;
typedef uint32 tint;
typedef uint32 aint;
typedef uint32 uintV;
typedef gcv_object_t object;
typedef struct {
  gcv_object_t cdr ;
  gcv_object_t car ;
} cons_;
typedef cons_ * Cons;
typedef struct {
  gcv_object_t GCself;
} symbol_;
typedef symbol_ * Symbol;
typedef struct {
  gcv_object_t GCself;
  gcv_object_t data[2] ;
} svector_;
typedef svector_ * Svector;
register gcv_object_t* STACK __asm__("%ebx");
extern object allocate_vector (uintL len);
struct symbol_tab_ {
  symbol_ S_nil;
} symbol_tab_data = { { (UBYTE*)&symbol_tab_data.S_nil+2 } };
extern void newinsert (object sym, uintL size);
extern void abort (void);
static gcv_object_t stackspace[100];
object rehash_symtab (object symtab)
{
  STACK[0] = symtab, STACK += 1;
  {
    uintL oldsize = ((uintV)(((oint)((((Svector)((oint)(symtab)-2))->data[0]))&((oint)0x20000000UL-1))>>5));
    {
      uintL newsize;
      {
        object size;
        STACK[0] = ((Svector)((oint)(symtab)-2))->data[1], STACK += 1;
        STACK[0] = (object)((UBYTE*)(&symbol_tab_data.S_nil)+2), STACK += 1;
        {
          uint32 prod = oldsize * 205UL;
          newsize = (prod < (1UL<<31) ? prod>>7 : 0xffffffUL);
        }
        newsize = (newsize - 1) | 1 ;
        size = ((gcv_object_t)((oint)(tint)0xC0000000 + ((oint)(aint)(newsize) << 5)));
        {
          object newtable = allocate_vector(newsize);
          STACK[0] = newtable, STACK += 1;
        }
        {
          gcv_object_t* offset = 0;
          uintL count;
          count = oldsize;
          do
            {
              object oldentry = *(gcv_object_t*)(((UBYTE*)(&((Svector)((oint)(((STACK[-3])))-2))->data[0])+((aint)offset)));
              if (((oint)(oldentry) & 3) == 2)
                do
                  {
                    STACK[0] = ((Cons)((oint)(oldentry)-2))->cdr, STACK += 1;
                    ((Cons)((oint)(oldentry)-2))->cdr = STACK[-3];
                    STACK[-3] = oldentry;
                    newinsert(((Cons)((oint)(oldentry)-2))->car,newsize);
                    oldentry = (STACK -= 1, STACK[0]);
                  }
                while (((oint)(oldentry) & 3) == 2);
              offset++;
            }
          while(!(--count==0));
        }
        if (STACK != &stackspace[54])
          abort();
        {
          object newtable = (STACK -= 1, STACK[0]);
          STACK -= 2;
          symtab = (STACK -= 1, STACK[0]);
          ((Svector)((oint)(symtab)-2))->data[0] = size;
          ((Svector)((oint)(symtab)-2))->data[1] = newtable;
        }
        return symtab;
      }
    }
  }
}
object allocate_vector (uintL len)
{
  return 0;
}
void newinsert (object sym, uintL size)
{
  if (STACK != &stackspace[55])
    abort();
}
static cons_ cons2 = { (void*)0, (void*)'2' };
static cons_ cons1 = { (UBYTE*)&cons2+2, (void*)'1' };
static svector_ vector1 = { (UBYTE*)&vector1+2, { (UBYTE*)&cons1+2, 0 } };
static svector_ symtab1 = { (UBYTE*)&symtab1+2, { (void*)0x20000020, (UBYTE*)&vector1+2 } };
int main ()
{
  STACK = &stackspace[50];
  rehash_symtab((UBYTE*)&symtab1+2);
  return 0;
}
============================================================================

This is a regression, because gcc 3.4.4, gcc 4.0.4, and gcc 4.1.2 all compile
this program fine, even with higher optimization levels (-O2).

What happens is that gcc 4.2.2 keeps a "cache" of the STACK = %ebx variable
in another register, %edi, in the inner loop, around the function call of
newinsert(). Two things are wrong here:
1) It is wrong to assume that newinsert() will leave STACK unmodified.
   newinsert() could also always decrement STACK by 2, for example. You can
   see that the code is unchanged if the newinsert() function is put into a
   different compilation unit.
2) The value cached in %edi and used for this statement
     oldentry = (STACK -= 1, STACK[0]);
   is the value of STACK at the beginning of the loop MINUS 1.
   If assuming that newinsert() does not change the STACK, the value cached
   in %edi should be the value of STACK at the beginning of the loop
   (since there is one increment of STACK before the newinsert call and
   one decrement of STACK after it.

Here's an annotated listing of the rehash_symtab function produced:
========================== gcc -O -S bug.c ================================
...
rehash_symtab:
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%edi
	pushl	%esi
	subl	$16, %esp
	movl	8(%ebp), %eax			; %eax := symtab
	movl	%eax, (%ebx)			; STACK[0] = symtab
	leal	4(%ebx), %ecx			; STACK += 1
	subl	$2, %eax
	movl	4(%eax), %edx
	andl	$536870911, %edx
	movl	%edx, %edi
	shrl	$5, %edi			; %edi := oldsize
	movl	8(%eax), %eax			; Get ((Svector)((oint)(symtab)-2))->data[1]
	movl	%eax, (%ecx)			; STACK[0] = ...
	addl	$4, %ecx			; STACK += 1
	movl	$symbol_tab_data+2, (%ecx)	; STACK[0] = (object)((UBYTE*)(&symbol_tab_data.S_nil)+2)
	leal	4(%ecx), %esi			; STACK += 1
	movl	%esi, %ebx
	imull	$205, %edi, %edx		; prod = oldsize * 205UL
	movl	$16777215, %eax
	testl	%edx, %edx
	js	.L10
	movl	%edx, %eax
	shrl	$7, %eax
.L10:						; newsize in %eax
	subl	$1, %eax
	orl	$1, %eax			; newsize = (newsize - 1) | 1
	movl	%eax, -12(%ebp)
	movl	%eax, (%esp)
	call	allocate_vector			; newtable = allocate_vector(newsize)
	movl	%eax, (%esi)			; STACK[0] = newtable
	addl	$4, %ebx			; STACK += 1
	movl	%edi, %esi
	movl	$0, -16(%ebp)			; offset = 0
.L11:
	movl	-12(%ebx), %eax
	movl	-16(%ebp), %ecx
	movl	2(%ecx,%eax), %edx		; %edx := oldentry
	movl	%edx, %eax
	andl	$3, %eax
	cmpl	$2, %eax
	jne	.L12				; (((oint)(oldentry) & 3) == 2)
	leal	-4(%ebx), %edi			; HERE! %edi := STACK - 1
.L19:						; BEGINNING OF INNER LOOP
	movl	-2(%edx), %eax
	movl	%eax, (%ebx)			; STACK[0] = ((Cons)((oint)(oldentry)-2))->cdr
	leal	4(%ebx), %eax
	movl	%eax, %ebx			; STACK += 1
	movl	-12(%eax), %eax
	movl	%eax, -2(%edx)			; ((Cons)((oint)(oldentry)-2))->cdr = STACK[-3]
	movl	%edx, -12(%ebx)			; STACK[-3] = oldentry
	movl	-12(%ebp), %eax			; newsize
	movl	%eax, 4(%esp)
	movl	2(%edx), %eax
	movl	%eax, (%esp)
	call	newinsert			; newinsert(((Cons)((oint)(oldentry)-2))->car,newsize)
	movl	%edi, %ebx			; HERE! BUG! WRONG VALUE FOR STACK!
	movl	(%edi), %edx			; oldentry = STACK[0]
	movl	%edx, %eax
	andl	$3, %eax
	cmpl	$2, %eax
	je	.L19				; (((oint)(oldentry) & 3) == 2)
.L12:						; END OF INNER LOOP
	subl	$1, %esi
	je	.L14				; while(!(--count==0))
	addl	$4, -16(%ebp)			; offset++
	jmp	.L11
.L14:
	cmpl	$stackspace+216, %ebx		; (STACK != &stackspace[54])
	.p2align 4,,2
	je	.L16
	call	abort
.L16:
	movl	stackspace+212, %esi
	movl	$stackspace+200, %ebx
	movl	stackspace+200, %eax
	leal	-2(%eax), %ecx
	movl	-12(%ebp), %edx
	sall	$5, %edx
	subl	$1073741824, %edx		; %edx := size
	movl	%edx, 4(%ecx)
	movl	%esi, 8(%ecx)
	addl	$16, %esp
	popl	%esi
	popl	%edi
	popl	%ebp
	ret
...
============================================================================
Comment 1 bruno 2007-11-30 10:46:49 UTC
Created attachment 14671 [details]
source code exhibiting the bug
Comment 2 Andrew Pinski 2007-11-30 10:50:02 UTC
hmm, i see some alias issues.
Comment 3 Richard Biener 2007-11-30 11:09:47 UTC
Works with trunk as well.  (Aliasing should be not the issue here, strict-aliasing
is off)
Comment 4 İsmail Dönmez 2007-11-30 11:56:44 UTC
Are we sure it works with trunk? Because this is initially found as a clisp crash bug and it still crashes with gcc 4.3 trunk but only when compiled with -O2.
Comment 5 İsmail Dönmez 2007-11-30 11:59:50 UTC
Indeed testcase doesn't abort with gcc 4.3 20071130 , so there must be another gcc bug hiding there :(
Comment 6 Rob 2009-03-13 20:30:21 UTC
Confirmed on the Trunk.

In the Bug mentioned at http://bugs.gentoo.org/54738 and here
this fails on gcc version 4.4.0 20090312 [trunk revision 144821].

Rob
Comment 7 H.J. Lu 2009-03-13 21:10:09 UTC
It works for me on Linux/Intel64 with gcc 4.4.0 revision 144836.
Comment 8 Jackie Rosen 2014-02-16 13:15:01 UTC Comment hidden (spam)