This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Serious code generation/optimisation bug (I think)
- From: <zoltan at bendor dot com dot au>
- To: <gcc at gcc dot gnu dot org>
- Date: Tue, 27 Jan 2009 18:52:18 +1100 (EST)
- Subject: Serious code generation/optimisation bug (I think)
I was debugging a function and by inserting the debug statement crashed
the system. Some investigation revealed that gcc 4.3.2 arm-eabi (compiled
from sources) with -O2 under some circumstances assumes that if a pointer
is dereferenced, it can not be NULL therefore explicite tests against
NULL can be later eliminated. Here is a short function that demonstrates
the erroneous behaviour:
extern void Debug( unsigned int x );
typedef struct s_head {
struct s_head *next;
unsigned int value;
} A_STRUCT;
void InsertByValue( A_STRUCT **queue, A_STRUCT *ptr )
{
A_STRUCT *tst;
for ( tst = *queue ; ; queue = &tst->next, tst = *queue ) {
// Debug( tst->value );
if ( ! tst ) {
ptr->next = (void *) 0;
break;
}
if ( tst->value < ptr->value ) {
ptr->next = tst;
break;
}
}
*queue = ptr;
}
Compiling this function with
arm-eabi-gcc -O2 -S foo.c
generates perfect code. However, if the Debug( tst->value ); is not
commented out, then the generated code looks like this:
InsertByValue:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, r5, r6, lr}
mov r6, r0
ldr r4, [r0, #0]
mov r5, r1
b .L3
.L2:
mov r6, r4
ldr r4, [r4, #0]
.L3:
ldr r0, [r4, #4]
bl Debug
ldr r2, [r4, #4]
ldr r3, [r5, #4]
cmp r2, r3
bcs .L2
str r4, [r5, #0]
str r5, [r6, #0]
ldmfd sp!, {r4, r5, r6, lr}
bx lr
As you can see, when 'tst' is fetched to R4, it is not checked against
being 0 anywhere and the whole if ( ! tst ) { ... } bit is completely
eliminated from the code. Indeed, the actual compiled code crashes because
the loop does not stop when the end of the list is reached.
I know that you are not supposed to dereference a NULL pointer, however,
on the microcontroller I have it is perfectly legal: what you get is an
element of the exception vector table that resides at 0x0.
I don't think that the compiler has a right to remove my test, just
because it assumes that if I derferenced a pointer then it surely was not
NULL. At least it should give me a warning (which it does not, not even
with -W -Wall -Wextra).
Zoltan