miscompilation of Freeciv with egcs and optimizer

Greg Wooledge gawooledge@sherwin.com
Tue Jul 28 13:32:00 GMT 1998


I got a core dump from the latest CVS Freeciv during some of the AI stuff;
the circumstances made no sense to me.  I played with gdb a bit and as
far as I can determine there seems to be an optimization error with egcs.
(I had been compiling with "-g -O2 -m486".  When I compiled with "-g"
the problem seemed to go away.)

The problem is triggered in get_defender() in unittools.c.  At first I
thought it was a problem with the unit_list_iterate macro expansion of
the complex expression map_get_tile(x,y)->units, so I changed the code
slightly for clarity:

-----------------------------------------------------------------------------
struct unit *get_defender(struct player *pplayer, struct unit *aunit, 
                          int x, int y)
{
  struct unit *bestdef = 0;
  int bestvalue=-1;
  struct unit_list ul = map_get_tile(x, y)->units;
  unit_list_iterate(ul, punit) {
    if (pplayer->player_no==punit->owner)
      return 0;
    if(unit_can_defend_here(punit) && rate_unit(punit, aunit)>bestvalue) {
      bestvalue=rate_unit(punit, aunit);
      bestdef=punit;
    }
  }
  unit_list_iterate_end;
  return bestdef;
}
-----------------------------------------------------------------------------

unit_list_iterate and unit_list_iterate_end are macros:

-----------------------------------------------------------------------------
#define unit_list_iterate(unitlist, punit) { \
  struct genlist_iterator myiter; \
  struct unit *punit; \
  genlist_iterator_init(&myiter, &unitlist.list, 0); \
  for(; ITERATOR_PTR(myiter);) { \
    punit=(struct unit *)ITERATOR_PTR(myiter); \
    ITERATOR_NEXT(myiter); 
#define unit_list_iterate_end }}
-----------------------------------------------------------------------------

map_get_tile has this prototype:

-----------------------------------------------------------------------------
struct tile *map_get_tile(int x, int y);
-----------------------------------------------------------------------------

My rewrite did not change the code's behavior, but it did allow me to
step through the code under gdb.  With "-g -O2 -m486":

-----------------------------------------------------------------------------
(gdb) n
297       struct unit_list ul = map_get_tile(x, y)->units;
(gdb) print x
$5 = 69
(gdb) n
298       unit_list_iterate(ul, punit) {
(gdb) print x
$6 = 7280
-----------------------------------------------------------------------------

Notice that x (which is passed by value to the map_get_tile function)
has changed!  (This causes, or is at least related to, the generation of
incorrect punit pointers.  One of these passed to unit_can_defend_here
is what causes the actual segmentation fault.)

With "-g":

-----------------------------------------------------------------------------
(gdb) n
297       struct unit_list ul = map_get_tile(x, y)->units;
(gdb) print x
$2 = 24
(gdb) n
p298      unit_list_iterate(ul, punit) {
(gdb) print x
$3 = 24
-----------------------------------------------------------------------------

In this case, x retains its value as it should.

I tried to look at the assembly code generated by egcs in both cases.
It is different, of course, with optimization turned on.  Unfortunately,
I don't understand x86 assembly well enough to find the problem.

Here's a portion of the assembly output from the get_defender function
compiled with "-g -O2 -m486" optimization.  I've only included the
output up to the call to genlist_iterator_init, which should be past
the point at which I was able to observe the problem under gdb.

-----------------------------------------------------------------------------
.stabs "get_defender:F(31,8)",36,0,294,get_defender
.stabs "pplayer:p(37,4)",160,0,292,8
.stabs "aunit:p(31,8)",160,0,292,12
.stabs "x:p(0,1)",160,0,293,16
.stabs "y:p(0,1)",160,0,293,20
.globl get_defender
        .type    get_defender,@function
get_defender:
.stabn 68,0,294,.LM119-get_defender
.LM119:
.LBB14:
.LBB15:
        pushl %ebp
        movl %esp,%ebp
        subl $40,%esp
        pushl %edi
        pushl %esi
        pushl %ebx
        movl 16(%ebp),%edx
        movl 20(%ebp),%eax
.stabn 68,0,295,.LM120-get_defender
.LM120:
        movl $0,-36(%ebp)
.stabn 68,0,296,.LM121-get_defender
.LM121:
        movl $-1,-40(%ebp)
.stabn 68,0,297,.LM122-get_defender
.LM122:
        pushl %eax
        pushl %edx
        call map_get_tile
        leal -24(%ebp),%edi
        leal 16(%eax),%esi
        cld
        movl $6,%ecx
        rep
        movsl
.stabn 68,0,298,.LM123-get_defender
.LM123:
        movl -36(%ebp),%ecx
        pushl %ecx
        leal -24(%ebp),%eax
        pushl %eax
        leal -32(%ebp),%eax
        pushl %eax
        call genlist_iterator_init
-----------------------------------------------------------------------------

Here are the details on this system:

-----------------------------------------------------------------------------
gemini:~/freeciv/server$ uname -a
Linux gemini 2.0.34 #4 Tue Jun 30 08:20:34 EDT 1998 i586
gemini:~/freeciv/server$ gcc -v
Reading specs from /usr/local/lib/gcc-lib/i586-pc-linux-gnu/egcs-2.90.29/specs
gcc version egcs-2.90.29 980515 (egcs-1.0.3 release)
gemini:~/freeciv/server$ ls -l /lib/libc.so.6*
lrwxrwxrwx   1 root     root           13 Jan 27  1998 /lib/libc.so.6 -> libc-2.0.6.so
gemini:~/freeciv/server$ ld -v
GNU ld version cygnus-2.8.1 (with BFD linux-2.8.1.0.15)
gemini:~/freeciv/server$ as -v
GNU assembler version 970731 (i586-pc-linux-gnu), using BFD version linux-2.8.1.0.15
-----------------------------------------------------------------------------

I'm in the process of putting together a small standalone program
which should exhibit (and hopefully isolate) the problem.  I'll send
that one just to the egcs-bugs list (the Freeciv folks already have a
large example...) unless I can get it *really* small, in which case
I may post to freeciv-dev as well.

My apologies to the Freeciv folks for the length and gory details of
this message, which has very little to do with the game -- but if others
are using egcs 1.0.3, this could be important.  My apologies to the egcs
folks if this is a well-known bug; I don't follow the egcs mailing lists,
and I'm not brave enough to use egcs snapshots.

Greg Wooledge
wooledge@kellnet.com
gwooledge@onenet-ici.com
gawooledge@sherwin.com



More information about the Gcc-bugs mailing list