http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51445

             Bug #: 51445
           Summary: g++ sometimes miscompiles code for the avr target
    Classification: Unclassified
           Product: gcc
           Version: 4.6.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
        AssignedTo: unassig...@gcc.gnu.org
        ReportedBy: dhowe...@redhat.com


Created attachment 26013
  --> http://gcc.gnu.org/bugzilla/attachment.cgi?id=26013
Test data: Reduced version of w5100.cpp

I'm trying to build stuff for my Arduino board, but the compiler occasionally
clobbers the return value of a function it just called.  This is notable in the
Arduino Ethernet library where the interactions with the W5100 chipset go
interestingly wrong.

Someone pointed me at a fix they'd come up with:

    http://code.google.com/p/arduino/issues/detail?id=605&start=200

whereby they observe the results of two 8-bit function calls are not being
correctly assembled into a 16-bit result in the following code:

    ...
    static uint16_t read##name(SOCKET _s) {                  \
        uint16_t res = readSn(_s, address);                  \
        res = (res << 8) + readSn(_s, address + 1);          \
        return res;                                          \
    }

However, replacing the above with:

    ...
    static uint16_t read##name(SOCKET _s) {                  \
        uint16_t res = readSn(_s, address);                  \
        uint16_t res2 = readSn(_s,address + 1);              \
        res = res << 8;                                      \
        res2 = res2 & 0xFF;                                  \
        res = res | res2;                                    \
        return res;                                          \
    }

Works.  I have also fallen foul of this problem.  They used 4.5.1 of their gcc,
I'm using 4.6.1:

Using built-in specs.
COLLECT_GCC=/usr/bin/avr-gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/avr/4.6.1/lto-wrapper
Target: avr
Configured with: ../gcc-4.6.1/configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --target=avr --enable-languages=c,c++ --disable-nls
--disable-libssp --with-system-zlib --enable-version-specific-runtime-libs
--with-pkgversion='Fedora 4.6.1-3.fc16'
--with-bugurl=https://bugzilla.redhat.com/
Thread model: single
gcc version 4.6.1 (Fedora 4.6.1-3.fc16) 

obtained by download from the Fedora 16 yum repository.  The associated
binutils is:

avr-binutils-2.20-2.fc16.x86_64

I've had a colleague build my testcase on a Debian system using the binary
packages available there and the resulting binary from that misbehaves in the
same way as building on Fedora.

Compiling the attached testcase file with the following command:

avr-gcc -g -Os -w -mmcu=atmega328p -ffunction-sections -fdata-sections -o tmp.o
-c tmp.cpp

and then disassembling the resulting object file:

avr-objdump -C -d tmp.o | less

I see that the W5100Class::send_data_processing() method incorrectly clobbers
the result of the first call to W5100Class::readSn() as made by
W5100::readSnTX_WR():

  26:   0e 94 00 00     call    0       ; 0x0
<W5100Class::send_data_processing(unsigned char, unsigned char*, unsigned int)>
  2a:   81 2f           mov     r24, r17
  2c:   65 e2           ldi     r22, 0x25       ; 37
  2e:   70 e0           ldi     r23, 0x00       ; 0
  30:   0e 94 00 00     call    0       ; 0x0
<W5100Class::send_data_processing(unsigned char, unsigned char*, unsigned int)>

The problem, if I understand the calling convention correctly, is that the
function returns its result in R24 - but this is being clobbered by the very
next instruction which overwrites it with the contents of R17 (the destination
register is supposed to be on the left).

I can't seem to remove much else from the test case without the problem going
away (snip off the implementation of W5100Class::execCmdSn() for example).

Also, there's a #if in testcase.  Flip the condition from 1 to 0 and the
resulting code is markedly different:

  24:   0e 94 00 00     call    0       ; 0x0
<W5100Class::send_data_processing(unsigned char, unsigned char*, unsigned int)>
  28:   e8 2e           mov     r14, r24
  2a:   81 2f           mov     r24, r17
  2c:   65 e2           ldi     r22, 0x25       ; 37
  2e:   70 e0           ldi     r23, 0x00       ; 0
  30:   0e 94 00 00     call    0       ; 0x0
<W5100Class::send_data_processing(unsigned char, unsigned char*, unsigned int)>

As can be seen, an extra line has appeared after the first CALL instruction,
saving the result of that call into R14 before copying R17 into R24.

Reply via email to