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