left-shift optimizer bug in avr-gcc (4.5.3)

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I seem to have found an optimizer bug in avr-gcc 4.5.3 (Linux)

When retrieving Xmega NVM bytes, I need to combine them into a 16-bit word, so routinely I wrote:

result <<= 8;

But this FAILED, storing zero into result.

After much testing, I nailed the problem to the optimizer which I had set to -O2. (Note: I re-tested that this method worked fine on my older distro which has avr-gcc version 4.3.5)

With a setting of -O0, -O1, or -O3 the code works fine.

The snippit below shows the code, and my final workaround. Each of the other commented-out lines under TEST-CODE FAIL with -O2, but they all work with the other settings.

uint16_t read_nvm_word( uint16_t address)
{
    uint16_t result;

    // read first byte (returns uint8_t)
    result = EEPROM_ReadByte( page, address);

    // store first byte in upper 8-bits
    // Note: looks silly, but at -O2 is the 
    // only thing that works!!!!!
    result <<= 7; result <<= 1;

    // --- TEST CODE (all work when OPT != -O2) ---
    // result = (uint16_t)result << (uint16_t)(0x0008);
    // result *= 256;
    // result = result << 8;

    
}
------assembly output------------
OPT = -O2
; this WORKS
    result <<= 7; result <<= 1;
       		mov r29,r18
       		clr r28
; these FAIL
    result <<= 8;
    OR
    result = result * 256;
       		ldi r24,lo8(0)
       		movw r28,r24

with OPT = -O3
; ALL these work
    result <<= 7; result <<= 1;
            mov r15,r18
            clr r14

    result <<= 8;
    OR
    result *= 256;
       		mov r17,r18
       		ldi r16,lo8(0)
Last Edited: Wed. Dec 28, 2011 - 06:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is "x"?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You have an example that can be compiled (need not be linkable)? Please no ... or similar like " " for extra obfuscation.

In you code above, page, EEPROM_ReadByte and other stuff is undefined so noone will be able to reproduce the artifact.

Moreover, if it's really a bug like PR46779, it's probably already fixed but not backported because of shortage of avr-gcc developers. So you use such disto or unpatched version at your own risk.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

[ FYI: I edited the previous post to remove the 'x' ]

Here's a compilable/runnable version.

I tested it on an STK600 with an xmega128a1.

My location 0 in the EEPROM contains 0x0a29. If the result is OK, the LED's show 0xa5, otherwise 0x01.

Just changing the commented code lines after "nop" will change it to working vs. non-working.

#include 
#include 

#define NVM_EXEC() asm("push r30" "\n\t"  \
                "push r31"      "\n\t"  \
                "push r16"      "\n\t"  \
                "push r18"      "\n\t"  \
                "ldi r30, 0xCB" "\n\t"  \
                "ldi r31, 0x01" "\n\t"  \
                "ldi r16, 0xD8" "\n\t"  \
                "ldi r18, 0x01" "\n\t"  \
                "out 0x34, r16" "\n\t"  \
                "st Z, r18"     "\n\t"  \
                "pop r18"       "\n\t"  \
                "pop r16"       "\n\t"  \
                "pop r31"       "\n\t"  \
                "pop r30"       "\n\t"  \
                )

unsigned char EEPROM_ReadByte( unsigned char pageAddr, unsigned char byteAddr )
{
	/* Wait until NVM is not busy. */
	do {} while ((NVM.STATUS & NVM_NVMBUSY_bm) == NVM_NVMBUSY_bm);

	/* Calculate address */
	unsigned int address = (unsigned int)(pageAddr*EEPROM_PAGE_SIZE)
	                            |(byteAddr & (EEPROM_PAGE_SIZE-1));

	/* Set 24-bit address to read from. */
	NVM.ADDR0 = address & 0xFF;
	NVM.ADDR1 = (address >> 8) & 0x1F;
	NVM.ADDR2 = 0x00;

	/* Issue EEPROM Read command. */
	NVM.CMD = NVM_CMD_READ_EEPROM_gc;
	NVM_EXEC();

	return NVM.DATA0;
}

unsigned int read_nvm( unsigned int address )
{
    unsigned int result;

    // double byte address as we store all 2-byte values
    address = address << 1; 
   
    result = EEPROM_ReadByte( 0, address);

    asm("nop"); // make this code easy to find
    // result <<= 8;  // using this line fails 
    result <<= 7; result <<= 1;  // this line works

    result += EEPROM_ReadByte( 0, address+1);

    return result;
}

int main (void)
{
    unsigned int result;

    PORTK.DIRSET = 0xff;
    PORTK.OUTSET = 0xff;

    result = read_nvm( 0 );

    if( result == 0x0a29 )
        PORTK.OUTCLR = 0xa5;
    else
        PORTK.OUTCLR = 0x01;

    while(1) 
        ;
    return 0;
}

Last Edited: Wed. Dec 28, 2011 - 06:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is no official avr-gcc release, so I cannot help you here.

Ask your package distribution provider or report the bug there.

From what can be seen from the distance it looks like PR46779 so you will have to switch to a version where that PR is fixed or backport the patch from 4.6.2 to your port

http://gcc.gnu.org/viewcvs?view=...

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
This is no official avr-gcc release, so I cannot help you here.

Ask your package distribution provider or report the bug there.

Hmmm. I run aptosid Linux which tends to have packages more on the bleeding edge (Sid repos). All I did was apt-get install gcc-avr to get the compiler, and this is what I got.

Perhaps I'll look around for a more stable release and use that instead.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kmceng wrote:
Perhaps I'll look around for a more stable release and use that instead.
http://www.wrightflyer.co.uk/avr-gcc/

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Got an error using dpkg -i so used doublecmd to open the .deb and extract the data.tar.gz and placed it manually.

I modified the eeprom read/write routines to use those in eeprom.h and all is well.

Thanks much for your build. I'm sure I'll make good use of it.