[En-Nut-Discussion] Just a word of warning with WinAVR-20050214.
Vesa Jääskeläinen
chaac at nic.fi
Mon May 9 23:09:55 CEST 2005
Ole Reinhardt wrote:
> Hi Vesa,
>
>> If we think about AHDLC's ioctl handler, at variable declarations it
>> have already referenced that data pointer. In case it was NULL it
>> reads four bytes from memory location 0. And this allowed GCC to
>> optimize NULL pointer check out of the picture.
>
> Could you please point me out a bit more detailed in whitch cases
> avr-gcc optimizes out NULL pointer checks? I use them quite often in
> some of my code...
Here is a test code I used:
--- begin of code ---
#define u_long unsigned long
#define u_char unsigned char
void test(int req, void *conf)
{
void **ppv = (void **) conf;
u_long *lvp = (u_long *) conf;
u_long lv = *lvp;
u_char bv = (u_char) lv;
switch (req) {
case 1:
if (bv)
{
asm ( "nop ; bv needs to be defined and used");
}
break;
case 2:
asm ( "nop ; entry to case 2");
/* bug: first compare for ppv is missing, second is only left */
if (ppv && (*ppv != 0)) {
asm ( "nop ; ppv and *ppv are not zeros");
} else {
asm ( "nop ; either ppv or *ppv is zero");
}
break;
}
}
--- end of code ---
I used following command line:
avr-gcc -c -mmcu=atmega128 -Os -Wall -Wstrict-prototypes
-Wa,-ahlms=test.lst test.c -o test.o
Now if you look at assembly output of that code, you will notice that
first compare to ppv is missing and only *ppv != 0 compare is done.
--- begin of assembly ---
.L5:
/* #APP */
nop ; entry to case 2
/* #NOAPP */
ld r24,Z
ldd r25,Z+1
or r24,r25
breq .L6
/* #APP */
nop ; ppv and *ppv are not zeros
/* #NOAPP */
--- end of assembly ---
Reason for this is that GCC's optimizer notices that notices ppv cannot
be NULL and therefore it removes whole clause as ppv != NULL is always
true. GCC is a bit inconsistent here and doesn't do this optimization
always so it only occures sometimes.
Reason for this behavior comes from fact that memory location by ppv is
accesses prior of this. In variable declaration:
u_long *lvp = (u_long *) conf;
u_long lv = *lvp;
u_char bv = (u_char) lv;
And as bv is used in code it then needs lv, and lv comes from pointer
value of lvp and lvp comes from conf and ppv shares ppv with lvp.
As it has already been read GCC assumes that this pointer must be valid
and not point to NULL.
And why the switch then fixes this:
--- begin of snippet from GCC manual ---
-fdelete-null-pointer-checks
Use global dataflow analysis to identify and eliminate useless checks
for null pointers. The compiler assumes that dereferencing a null
pointer would have halted the program. If a pointer is checked after it
has already been dereferenced, it cannot be null.
In some environments, this assumption is not true, and programs can
safely dereference null pointers. Use -fno-delete-null-pointer-checks to
disable this optimization for programs which depend on that behavior.
Enabled at levels -O2, -O3, -Os.
--- end of snippet from GCC manual ---
I hope this gives more light on situation :)
Thanks,
Vesa Jääskeläinen
More information about the En-Nut-Discussion
mailing list