Dear GCC folks,
I think I found a code generation bug in GCC 4.0.1.
I'm sending this mail to make sure this bug is known and
is or will be removed in newer versions.
On a quick glance I couldn't find it in the bug database,
so it may not be known.
***
I am developing a simulation program that appears to show a
bug when compiled with gcc 4.0.1 (cross-compiler on Linux
for MINGW target) but compiles fine with GCC 3 or GCC 4.1.2
(native on Linux) as well as with gcc 3 mingw on Cygwin/Windows.
For the 4.0.1 cross-compiled program, the bug disappears
when the function is duplicated under another name, and
the duplicate is called instead in the critical place.
The original (buggy) function is not inlined, but the
duplicate is inlined because it's only called in one
place.
The function is an AVL tree deletion which operates
with pointers only.
I attach the C source code and the assembler code (output of
objdump -d) to this mail. I have marked the two places in C
where I think the bug appears with a comment containing "BUG".
I'm not good enough in assembler to verify the bug there.
What seems to happen is that the value of ta, which should be
set to the address of the parent node's left-child pointer in
this case, remains unchanged, i.e. remains pointing to 'dummy'.
Then, in the subsequent assignment *ta = r the pointer does
not get changed appropriately.
Before filing this as a bug, I'd like some person who can
read assembler to examine the situation.
I'll gladly assist with further information.
***
The code is compiled with this command:
i386-pc-mingw32-gcc -DHAVE_CONFIG_H -I. -I../../bugsklsim/src -I..
-std=gnu9x -W -Wall -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes
-Wbad-function-cast -Wcast-qual -Wcast-align -Wmissing-declarations
-Wnested-externs -Winline -Waggregate-return -Wshadow -g -O2 -MT sklsim.o -MD
-MP -MF ".deps/sklsim.Tpo" -c -o sklsim.o ../../bugsklsim/src/sklsim.c
Best regards,
Claus Fischer
--
Claus Fischer <[EMAIL PROTECTED]>
http://www.clausfischer.com/
typedef struct sklsim_event_s {
/* AVL tree */
int ev_bal; /* Tree balance: -1 left 0 middle 1 right */
struct sklsim_event_s *ev_l;/* Left subtree */
struct sklsim_event_s *ev_r;/* Right subtree */
struct sklsim_event_s *ev_p;/* Parent */
/* Those members not used in deletend function: */
double t; /* Time of next event */
sklsim_otype_t otype; /* Object type: OTYPE_PAX, OTYPE_AC, ... */
} sklsim_event_t;
/* AVL Tree: Delete node d from the tree. Node d must be in the tree.
* Return a pointer to the tree root, or to the top node of the tree.
* For root-less trees, return the new tree pointer.
*/
static sklsim_event_t * eventtree_deletend( sklsim_event_t * d)
{
sklsim_event_t * s; /* Node to be removed */
sklsim_event_t * su; /* Parent of s */
sklsim_event_t * sc; /* Child of s */
/* Nodes for the rebalancing loop */
sklsim_event_t * t;
sklsim_event_t * x;
/* The algorithm removes element d from the tree. If d has
two children, the algorithm first removes d's next left
neighbour, s, which is the rightmost node in d's left
subtree. At the very end, s is put in d's place in the
tree. */
/* (1) Find node s to remove instead of d */
/* ************************************** */
/* Find node s such that
* - if d has not two children, s is d
* - else s is the immediate left neighbour of d
* - s has one child node or none
*/
s = d;
if (s->ev_r != 0)
{
sklsim_event_t * ss;
ss = s->ev_l;
while (ss != 0)
{
s = ss;
ss = s->ev_r;
}
}
/* (2) Adjust the tree above s to reflect the future reduced height of s */
/* ********************************************************************* */
/* State:
* The height of the subtree rooted at s will be reduced in (3)
* when s will be removed. Before that happens, the balances
* of all nodes above s need to be adjusted.
* In the loop, the height of s is considered to be reduced already.
*/
/* The loop reduces height of subtree x under its parent t */
x = s;
t = s->ev_p;
while (t != 0)
{
sklsim_event_t * tu; /* parent of subtree t */
sklsim_event_t * *ta; /* address of pointer to subtree t */
sklsim_event_t * dummy;
/* Parent and address of t */
tu = t->ev_p;
if (tu != 0)
{
if (tu->ev_l == t)
ta = &tu->ev_l; /* BUG HAPPENS HERE, I THINK */
else
ta = &tu->ev_r;
}
else
{
ta = &dummy;
}
/* State:
* - The height of x has been reduced.
* - t is the parent of x.
* - t's subtree will be rearranged (including t)
* - tu is the parent of t or NIL
* - ta is the address of the parent's pointer to t
* After rearrangement:
* - the new subtree will be hooked under ta
* - if the subtree under ta has lost height:
* x is set to the new subtree under ta
* and the loop will continue
* - if the subtree under ta has kept its height:
* the loop is ended
*/
/* x is left subtree of t */
if (x == t->ev_l)
{
/* No rotation */
if (t->ev_bal == 0)
{
t->ev_bal = 1;
break;
}
/* No rotation */
else if (t->ev_bal == -1)
{
t->ev_bal = 0;
x = t;
}
else /* right balanced */
{
sklsim_event_t * r = t->ev_r;
sklsim_event_t * m = r->ev_l;
/* Single rotation */
if (r->ev_bal != -1)
{
/* r's balance can be right or middle */
*ta = r; /* BUG MANIFESTS HERE */
r->ev_p = tu;
t->ev_p = r;
if (m != 0) m->ev_p = t;
t->ev_r = m;
r->ev_l = t;
/* r's balance can be middle or right */
t->ev_bal =
(r->ev_bal == 0 ?
1 : 0);
r->ev_bal =
(r->ev_bal == 0 ?
-1 : 0);
if (r->ev_bal != 0)
break; /* r's height same as t's height before */
x = r;
}
/* Double rotation (*m guaranteed to exist) */
else
{
*ta = m;
m->ev_p = tu;
t->ev_p = m;
r->ev_p = m;
t->ev_r = m->ev_l;
if (m->ev_l != 0) m->ev_l->ev_p = t;
m->ev_l = t;
r->ev_l = m->ev_r;
if (m->ev_r != 0) m->ev_r->ev_p = r;
m->ev_r = r;
/* m's balance can be left, middle, or right */
t->ev_bal =
(m->ev_bal == 1 ?
-1 : 0);
r->ev_bal =
(m->ev_bal == -1 ?
1 : 0);
m->ev_bal = 0;
x = m;
}
}
}
/* x is right subtree of t */
else if (x == t->ev_r)
{
/* No rotation */
if (t->ev_bal == 0)
{
t->ev_bal = -1;
break;
}
/* No rotation */
else if (t->ev_bal == 1)
{
t->ev_bal = 0;
x = t;
}
else /* left balanced */
{
sklsim_event_t * l = t->ev_l;
sklsim_event_t * m = l->ev_r;
/* Single rotation */
if (l->ev_bal != 1)
{
/* l's balance can be left or middle */
*ta = l;
l->ev_p = tu;
t->ev_p = l;
if (m != 0)
m->ev_p = t;
t->ev_l = m;
l->ev_r = t;
/* l's balance can be middle or left */
t->ev_bal =
(l->ev_bal == 0 ?
-1 : 0);
l->ev_bal =
(l->ev_bal == 0 ?
1 : 0);
if (l->ev_bal != 0)
break; /* l's height same as t's height before */
x = l;
}
/* Double rotation (*m guaranteed to exist) */
else
{
*ta = m;
m->ev_p = tu;
t->ev_p = m;
l->ev_p = m;
t->ev_l = m->ev_r;
if (m->ev_r != 0)
m->ev_r->ev_p = t;
m->ev_r = t;
l->ev_r = m->ev_l;
if (m->ev_l != 0)
m->ev_l->ev_p = l;
m->ev_l = l;
/* m's balance can be left, middle, or right */
t->ev_bal =
(m->ev_bal == -1 ?
1 : 0);
l->ev_bal =
(m->ev_bal == 1 ?
-1 : 0);
m->ev_bal = 0;
x = m;
}
}
}
/* State:
* The subtree under ta/tu is rearranged as described above.
* Set t to the parent of x, and re-run the loop.
*/
t = tu;
}
/* State:
* The tree above s is rebalanced and readjusted
* for the decreased height of the subtree under s.
*
* In the current tree, the balance of the parent of s
* is temporarily inconsistent; it already reflects the
* new height of the subtree of s.
*/
/* (3) Remove s from the tree */
/* ************************** */
/* Remove s from the tree and put its child sc in its place */
/* Remember s has only one child, or none at all */
if (s->ev_l != 0)
sc = s->ev_l;
else
sc = s->ev_r;
su = s->ev_p;
if (su != 0)
{
if (su->ev_l == s)
su->ev_l = sc;
else
su->ev_r = sc;
}
if (sc != 0)
{
sc->ev_p = su;
}
/* State:
*
* The tree is correctly balanced now, and s is removed
* from the tree. The node su is the former parent of s.
*/
/* (4) Substitute s for d */
/* ********************** */
if (s != d)
{
sklsim_event_t * l;
sklsim_event_t * r;
/* Substitute s for d */
su = d->ev_p;
if (su != 0)
{
if (su->ev_l == d)
su->ev_l = s;
else
su->ev_r = s;
}
s->ev_p = su;
l = d->ev_l;
s->ev_l = l;
if (l != 0) l->ev_p = s;
r = d->ev_r;
s->ev_r = r;
if (r != 0) r->ev_p = s;
s->ev_bal = d->ev_bal;
}
else
s = sc;
/* State:
*
* - d is removed from the tree
* - su is the former parent of the deleted node d.
* - s is a node in the tree (child of su) or NIL.
* - the tree is properly balanced
* - the subtree up to s is fully correct
*/
/* (5) Clean the deleted node */
/* ************************** */
d->ev_p = 0;
d->ev_l = 0;
d->ev_r = 0;
d->ev_bal = 0;
/* (6) Find top of tree */
/* ******************** */
/* Make s the top node of the tree, or NIL */
while (su != 0)
{
s = su;
su = su->ev_p;
}
return su != 0 ? su : s;
}
sklsim_simulate.o: file format pe-i386
Disassembly of section .text:
000003ac <_eventtree_deletend>:
3ac: 55 push %ebp
3ad: 89 e5 mov %esp,%ebp
3af: 57 push %edi
3b0: 56 push %esi
3b1: 53 push %ebx
3b2: 52 push %edx
3b3: 89 45 f0 mov %eax,0xfffffff0(%ebp)
3b6: 8b 78 08 mov 0x8(%eax),%edi
3b9: 85 ff test %edi,%edi
3bb: 0f 84 9a 01 00 00 je 55b <_eventtree_deletend+0x1af>
3c1: 8b 40 04 mov 0x4(%eax),%eax
3c4: 85 c0 test %eax,%eax
3c6: 75 0a jne 3d2 <_eventtree_deletend+0x26>
3c8: e9 8e 01 00 00 jmp 55b <_eventtree_deletend+0x1af>
3cd: 8d 76 00 lea 0x0(%esi),%esi
3d0: 89 d0 mov %edx,%eax
3d2: 8b 50 08 mov 0x8(%eax),%edx
3d5: 85 d2 test %edx,%edx
3d7: 75 f7 jne 3d0 <_eventtree_deletend+0x24>
3d9: 89 c6 mov %eax,%esi
3db: 8b 4e 0c mov 0xc(%esi),%ecx
3de: 89 ca mov %ecx,%edx
3e0: 85 c9 test %ecx,%ecx
3e2: 74 77 je 45b <_eventtree_deletend+0xaf>
3e4: 89 f0 mov %esi,%eax
3e6: eb 0f jmp 3f7 <_eventtree_deletend+0x4b>
3e8: 3b 42 08 cmp 0x8(%edx),%eax
3eb: 0f 84 0f 01 00 00 je 500 <_eventtree_deletend+0x154>
3f1: 89 fa mov %edi,%edx
3f3: 85 ff test %edi,%edi
3f5: 74 61 je 458 <_eventtree_deletend+0xac>
3f7: 8b 7a 0c mov 0xc(%edx),%edi
3fa: 8b 4a 04 mov 0x4(%edx),%ecx
3fd: 39 c8 cmp %ecx,%eax
3ff: 75 e7 jne 3e8 <_eventtree_deletend+0x3c>
401: 8b 02 mov (%edx),%eax
403: 85 c0 test %eax,%eax
405: 0f 84 0d 02 00 00 je 618 <_eventtree_deletend+0x26c>
40b: 40 inc %eax
40c: 0f 84 3c 01 00 00 je 54e <_eventtree_deletend+0x1a2>
412: 8b 4a 08 mov 0x8(%edx),%ecx
415: 8b 59 04 mov 0x4(%ecx),%ebx
418: 8b 01 mov (%ecx),%eax
41a: 83 f8 ff cmp $0xffffffff,%eax
41d: 0f 84 40 01 00 00 je 563 <_eventtree_deletend+0x1b7>
423: 89 79 0c mov %edi,0xc(%ecx)
426: 89 4a 0c mov %ecx,0xc(%edx)
429: 85 db test %ebx,%ebx
42b: 74 03 je 430 <_eventtree_deletend+0x84>
42d: 89 53 0c mov %edx,0xc(%ebx)
430: 89 5a 08 mov %ebx,0x8(%edx)
433: 89 51 04 mov %edx,0x4(%ecx)
436: 85 c0 test %eax,%eax
438: 0f 94 c0 sete %al
43b: 0f b6 c0 movzbl %al,%eax
43e: 89 02 mov %eax,(%edx)
440: 8b 01 mov (%ecx),%eax
442: 83 f8 01 cmp $0x1,%eax
445: 19 c0 sbb %eax,%eax
447: 89 01 mov %eax,(%ecx)
449: 85 c0 test %eax,%eax
44b: 75 0b jne 458 <_eventtree_deletend+0xac>
44d: 89 c8 mov %ecx,%eax
44f: 89 fa mov %edi,%edx
451: 85 ff test %edi,%edi
453: 75 a2 jne 3f7 <_eventtree_deletend+0x4b>
455: 8d 76 00 lea 0x0(%esi),%esi
458: 8b 4e 0c mov 0xc(%esi),%ecx
45b: 8b 46 04 mov 0x4(%esi),%eax
45e: 85 c0 test %eax,%eax
460: 0f 84 9a 01 00 00 je 600 <_eventtree_deletend+0x254>
466: 89 ca mov %ecx,%edx
468: 85 c9 test %ecx,%ecx
46a: 74 0c je 478 <_eventtree_deletend+0xcc>
46c: 3b 71 04 cmp 0x4(%ecx),%esi
46f: 0f 84 93 01 00 00 je 608 <_eventtree_deletend+0x25c>
475: 89 41 08 mov %eax,0x8(%ecx)
478: 85 c0 test %eax,%eax
47a: 74 03 je 47f <_eventtree_deletend+0xd3>
47c: 89 48 0c mov %ecx,0xc(%eax)
47f: 3b 75 f0 cmp 0xfffffff0(%ebp),%esi
482: 0f 84 71 01 00 00 je 5f9 <_eventtree_deletend+0x24d>
488: 8b 4d f0 mov 0xfffffff0(%ebp),%ecx
48b: 8b 51 0c mov 0xc(%ecx),%edx
48e: 85 d2 test %edx,%edx
490: 74 0c je 49e <_eventtree_deletend+0xf2>
492: 3b 4a 04 cmp 0x4(%edx),%ecx
495: 0f 84 75 01 00 00 je 610 <_eventtree_deletend+0x264>
49b: 89 72 08 mov %esi,0x8(%edx)
49e: 89 56 0c mov %edx,0xc(%esi)
4a1: 8b 4d f0 mov 0xfffffff0(%ebp),%ecx
4a4: 8b 41 04 mov 0x4(%ecx),%eax
4a7: 89 46 04 mov %eax,0x4(%esi)
4aa: 85 c0 test %eax,%eax
4ac: 74 03 je 4b1 <_eventtree_deletend+0x105>
4ae: 89 70 0c mov %esi,0xc(%eax)
4b1: 8b 4d f0 mov 0xfffffff0(%ebp),%ecx
4b4: 8b 41 08 mov 0x8(%ecx),%eax
4b7: 89 46 08 mov %eax,0x8(%esi)
4ba: 85 c0 test %eax,%eax
4bc: 74 03 je 4c1 <_eventtree_deletend+0x115>
4be: 89 70 0c mov %esi,0xc(%eax)
4c1: 8b 4d f0 mov 0xfffffff0(%ebp),%ecx
4c4: 8b 01 mov (%ecx),%eax
4c6: 89 06 mov %eax,(%esi)
4c8: 8b 45 f0 mov 0xfffffff0(%ebp),%eax
4cb: c7 40 0c 00 00 00 00 movl $0x0,0xc(%eax)
4d2: c7 40 04 00 00 00 00 movl $0x0,0x4(%eax)
4d9: c7 40 08 00 00 00 00 movl $0x0,0x8(%eax)
4e0: c7 00 00 00 00 00 movl $0x0,(%eax)
4e6: 85 d2 test %edx,%edx
4e8: 75 04 jne 4ee <_eventtree_deletend+0x142>
4ea: eb 0b jmp 4f7 <_eventtree_deletend+0x14b>
4ec: 89 c2 mov %eax,%edx
4ee: 8b 42 0c mov 0xc(%edx),%eax
4f1: 85 c0 test %eax,%eax
4f3: 75 f7 jne 4ec <_eventtree_deletend+0x140>
4f5: 89 d6 mov %edx,%esi
4f7: 89 f0 mov %esi,%eax
4f9: 5e pop %esi
4fa: 5b pop %ebx
4fb: 5e pop %esi
4fc: 5f pop %edi
4fd: c9 leave
4fe: c3 ret
4ff: 90 nop
500: 8b 02 mov (%edx),%eax
502: 85 c0 test %eax,%eax
504: 0f 84 1c 01 00 00 je 626 <_eventtree_deletend+0x27a>
50a: 48 dec %eax
50b: 74 41 je 54e <_eventtree_deletend+0x1a2>
50d: 8b 59 08 mov 0x8(%ecx),%ebx
510: 8b 01 mov (%ecx),%eax
512: 83 f8 01 cmp $0x1,%eax
515: 0f 84 93 00 00 00 je 5ae <_eventtree_deletend+0x202>
51b: 89 79 0c mov %edi,0xc(%ecx)
51e: 89 4a 0c mov %ecx,0xc(%edx)
521: 85 db test %ebx,%ebx
523: 74 03 je 528 <_eventtree_deletend+0x17c>
525: 89 53 0c mov %edx,0xc(%ebx)
528: 89 5a 04 mov %ebx,0x4(%edx)
52b: 89 51 08 mov %edx,0x8(%ecx)
52e: 83 f8 01 cmp $0x1,%eax
531: 19 c0 sbb %eax,%eax
533: 89 02 mov %eax,(%edx)
535: 31 c0 xor %eax,%eax
537: 83 39 00 cmpl $0x0,(%ecx)
53a: 0f 94 c0 sete %al
53d: 89 01 mov %eax,(%ecx)
53f: 85 c0 test %eax,%eax
541: 0f 85 11 ff ff ff jne 458 <_eventtree_deletend+0xac>
547: 89 c8 mov %ecx,%eax
549: e9 01 ff ff ff jmp 44f <_eventtree_deletend+0xa3>
54e: c7 02 00 00 00 00 movl $0x0,(%edx)
554: 89 d0 mov %edx,%eax
556: e9 96 fe ff ff jmp 3f1 <_eventtree_deletend+0x45>
55b: 8b 75 f0 mov 0xfffffff0(%ebp),%esi
55e: e9 78 fe ff ff jmp 3db <_eventtree_deletend+0x2f>
563: 89 7b 0c mov %edi,0xc(%ebx)
566: 89 5a 0c mov %ebx,0xc(%edx)
569: 89 59 0c mov %ebx,0xc(%ecx)
56c: 8b 43 04 mov 0x4(%ebx),%eax
56f: 89 42 08 mov %eax,0x8(%edx)
572: 85 c0 test %eax,%eax
574: 74 03 je 579 <_eventtree_deletend+0x1cd>
576: 89 50 0c mov %edx,0xc(%eax)
579: 89 53 04 mov %edx,0x4(%ebx)
57c: 8b 43 08 mov 0x8(%ebx),%eax
57f: 89 41 04 mov %eax,0x4(%ecx)
582: 85 c0 test %eax,%eax
584: 74 03 je 589 <_eventtree_deletend+0x1dd>
586: 89 48 0c mov %ecx,0xc(%eax)
589: 89 4b 08 mov %ecx,0x8(%ebx)
58c: 31 c0 xor %eax,%eax
58e: 83 3b 01 cmpl $0x1,(%ebx)
591: 0f 95 c0 setne %al
594: 48 dec %eax
595: 89 02 mov %eax,(%edx)
597: 31 c0 xor %eax,%eax
599: 83 3b ff cmpl $0xffffffff,(%ebx)
59c: 0f 94 c0 sete %al
59f: 89 01 mov %eax,(%ecx)
5a1: c7 03 00 00 00 00 movl $0x0,(%ebx)
5a7: 89 d8 mov %ebx,%eax
5a9: e9 43 fe ff ff jmp 3f1 <_eventtree_deletend+0x45>
5ae: 89 7b 0c mov %edi,0xc(%ebx)
5b1: 89 5a 0c mov %ebx,0xc(%edx)
5b4: 89 59 0c mov %ebx,0xc(%ecx)
5b7: 8b 43 08 mov 0x8(%ebx),%eax
5ba: 89 42 04 mov %eax,0x4(%edx)
5bd: 85 c0 test %eax,%eax
5bf: 74 03 je 5c4 <_eventtree_deletend+0x218>
5c1: 89 50 0c mov %edx,0xc(%eax)
5c4: 89 53 08 mov %edx,0x8(%ebx)
5c7: 8b 43 04 mov 0x4(%ebx),%eax
5ca: 89 41 08 mov %eax,0x8(%ecx)
5cd: 85 c0 test %eax,%eax
5cf: 74 03 je 5d4 <_eventtree_deletend+0x228>
5d1: 89 48 0c mov %ecx,0xc(%eax)
5d4: 89 4b 04 mov %ecx,0x4(%ebx)
5d7: 31 c0 xor %eax,%eax
5d9: 83 3b ff cmpl $0xffffffff,(%ebx)
5dc: 0f 94 c0 sete %al
5df: 89 02 mov %eax,(%edx)
5e1: 31 c0 xor %eax,%eax
5e3: 83 3b 01 cmpl $0x1,(%ebx)
5e6: 0f 95 c0 setne %al
5e9: 48 dec %eax
5ea: 89 01 mov %eax,(%ecx)
5ec: c7 03 00 00 00 00 movl $0x0,(%ebx)
5f2: 89 d8 mov %ebx,%eax
5f4: e9 f8 fd ff ff jmp 3f1 <_eventtree_deletend+0x45>
5f9: 89 c6 mov %eax,%esi
5fb: e9 c8 fe ff ff jmp 4c8 <_eventtree_deletend+0x11c>
600: 8b 46 08 mov 0x8(%esi),%eax
603: e9 5e fe ff ff jmp 466 <_eventtree_deletend+0xba>
608: 89 41 04 mov %eax,0x4(%ecx)
60b: e9 68 fe ff ff jmp 478 <_eventtree_deletend+0xcc>
610: 89 72 04 mov %esi,0x4(%edx)
613: e9 86 fe ff ff jmp 49e <_eventtree_deletend+0xf2>
618: c7 02 01 00 00 00 movl $0x1,(%edx)
61e: 8b 4e 0c mov 0xc(%esi),%ecx
621: e9 35 fe ff ff jmp 45b <_eventtree_deletend+0xaf>
626: c7 02 ff ff ff ff movl $0xffffffff,(%edx)
62c: 8b 4e 0c mov 0xc(%esi),%ecx
62f: e9 27 fe ff ff jmp 45b <_eventtree_deletend+0xaf>