[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