cgccavr-gccgcc4winavr

lpm rd,Z always translates to lpm rd,Z+ on inline assembly in gcc-avr


This is a follow up question to this. I'm writing code for an __AVR_HAVE_LPMX__ processor (avr25) using

when I use lpm rd, Z in inline assembly, the compiler always translates this to lpm rd,Z+ (excerpt from lss-file):

asm volatile("lpm r24,Z");
248:    84 91           lpm r24, Z+

which is bad, if it's used in successive access to a look-up-table. The look-up value might be 0xff, so this unnecessarily increments ZH (r31) thus corrupting this solution.

Any suggestions to circumvent this behavior?


Solution

  • There is nothing wrong in the solution; your disassembler (avr-objdump -d, part of binutils package) is buggy.

    See page 97 of the Atmel AVR instruction set manual (PDF). The lpm instruction variants are encoded as

    1001 0101 1100 1000 = 0x95C8   lpm r0,Z
    1001 000? ???? 0100 = 0x9??4   lpm r?,Z
    1001 000? ???? 0101 = 0x9??5   lpm r?,Z+
    

    Assuming we trust the Atmel documentation better than your disassembler, then

    84 91    lpm r24,Z
    

    whereas

    85 91    lpm r24,Z+
    

    Indeed, avr-gcc (GCC) 4.8.2 compiles the inline assembly to the same two bytes (84 91) using avr-gcc-4.8.2 -O2 -fomit-frame-pointer -mmcu=avr25, and lists it as lpm r24,Z in the assembly source file (using -S option); when compiled to an object file, and disassembling with avr-objdump (GNU binutils) 2.23.1 using avr-objdump -d, the instruction is still 84 91 lpm r24,Z.

    This makes me believe it is a bug in avr-objdump (part of GNU binutils). Ah yes, reported here, and apparently fixed in binutils-2.23.1 in October 2013.

    In short, only the disassembly is affected; the disassembly incorrectly shows Z+ when it should show Z. It does not affect the generated code, only the human-readable output is incorrect. To fix, upgrade to binutils version 2.23.1 or later. If you can't, don't worry: you can safely ignore this error, as it only affects the human-readable disassembly.

    Questions?