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.