https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77326
Bug ID: 77326 Summary: [avr] Invalid optimization using varargs and a weak function Product: gcc Version: 5.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: target Assignee: unassigned at gcc dot gnu.org Reporter: matthijs at stdin dot nl Target Milestone: --- Created attachment 39483 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=39483&action=edit Preprocessed source generated by avr-gcc foo.c -Dissue -save-temps This bug was originally reported to the Arduino bug tracker[1], but seems to be a avr-specific gcc bug. A minimal program showing the problem: #include <stddef.h> #include <stdarg.h> void test(void) __attribute__((weak)); void va_pseudo(int flag,...){ va_list ap; va_start (ap, flag); va_end (ap); } int main(void) { #if defined(issue) va_pseudo(1, 2, 3, 4); #else va_pseudo(1, 2, 3); #endif if(test!=NULL) { test(); } return 0; } When compiled with -O but without -Dissue, this produces the following assembler: $ avr-gcc foo.c -O; avr-objdump -d a.out a.out: file format elf32-avr Disassembly of section .text: 00000000 <va_pseudo>: 0: cf 93 push r28 2: df 93 push r29 4: cd b7 in r28, 0x3d ; 61 6: de b7 in r29, 0x3e ; 62 8: df 91 pop r29 a: cf 91 pop r28 c: 08 95 ret 0000000e <main>: e: 1f 92 push r1 10: 83 e0 ldi r24, 0x03 ; 3 12: 8f 93 push r24 14: 1f 92 push r1 16: 82 e0 ldi r24, 0x02 ; 2 18: 8f 93 push r24 1a: 1f 92 push r1 1c: 81 e0 ldi r24, 0x01 ; 1 1e: 8f 93 push r24 20: ef df rcall .-34 ; 0x0 <va_pseudo> 22: 0f 90 pop r0 24: 0f 90 pop r0 26: 0f 90 pop r0 28: 0f 90 pop r0 2a: 0f 90 pop r0 2c: 0f 90 pop r0 2e: 80 e0 ldi r24, 0x00 ; 0 30: 90 e0 ldi r25, 0x00 ; 0 32: 89 2b or r24, r25 34: 09 f0 breq .+2 ; 0x38 <main+0x2a> 36: e4 df rcall .-56 ; 0x0 <va_pseudo> 38: 80 e0 ldi r24, 0x00 ; 0 3a: 90 e0 ldi r25, 0x00 ; 0 3c: 08 95 ret Note the lines from 0x2e to 0x34, which implement the `if(test!=NULL)`, which should of course always fail and skip the next `rcall`. Now, when compiling this with -Dissue, the `or r24, r25` line gets dropped, making the generated code invalid: $ avr-gcc foo.c -O -Dissue; avr-objdump -d a.out | grep -B 2 breq 38: 80 e0 ldi r24, 0x00 ; 0 3a: 90 e0 ldi r25, 0x00 ; 0 3c: 09 f0 breq .+2 ; 0x40 <__SREG__+0x1> The diff between without and with -Dissue looks like this (jump addresses have been stripped to minimize the diff): @@ -15,6 +15,9 @@ <va_pseudo>: <main>: 1f 92 push r1 + 84 e0 ldi r24, 0x04 ; 4 + 8f 93 push r24 + 1f 92 push r1 83 e0 ldi r24, 0x03 ; 3 8f 93 push r24 1f 92 push r1 @@ -24,16 +27,17 @@ <main>: 81 e0 ldi r24, 0x01 ; 1 8f 93 push r24 xx xx rcall ; <va_pseudo> - 0f 90 pop r0 - 0f 90 pop r0 - 0f 90 pop r0 - 0f 90 pop r0 - 0f 90 pop r0 - 0f 90 pop r0 + 8d b7 in r24, 0x3d ; 61 + 9e b7 in r25, 0x3e ; 62 + 08 96 adiw r24, 0x08 ; 8 + 0f b6 in r0, 0x3f ; 63 + f8 94 cli + 9e bf out 0x3e, r25 ; 62 + 0f be out 0x3f, r0 ; 63 + 8d bf out 0x3d, r24 ; 61 80 e0 ldi r24, 0x00 ; 0 90 e0 ldi r25, 0x00 ; 0 - 89 2b or r24, r25 xx xx breq ; <main+0x....> xx xx rcall ; <va_pseudo> 80 e0 ldi r24, 0x00 ; 0 90 e0 ldi r25, 0x00 ; 0 As you can see, the extra vararg changes the stack cleanup from a number of pops to direct manipulation of the stack pointer, which involves the same registers (r24 and r25) as the `test` check. When running without -O, this bug does not occur. Then, the check looks like this: $ avr-gcc foo.c -Dissue; avr-objdump -d a.out | grep -B 3 breq 50: 80 e0 ldi r24, 0x00 ; 0 52: 90 e0 ldi r25, 0x00 ; 0 54: 00 97 sbiw r24, 0x00 ; 0 56: 09 f0 breq .+2 ; 0x5a <__SREG__+0x1b> Here, the check uses the (slightly slower) sbiw instruction, where the -O version uses or. I suspect that the optimization that makes this change is responsible for, or at least involved in the bug. I couldn't pinpoint the exact optimization responsible, running without -O, but with all the options that -O is documented to turn on did not produce the bug. The above was tested using: Using built-in specs. COLLECT_GCC=avr-gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/avr/4.8.1/lto-wrapper Target: avr Configured with: ../src/configure -v --enable-languages=c,c++ --prefix=/usr/lib --infodir=/usr/share/info --mandir=/usr/share/man --bindir=/usr/bin --libexecdir=/usr/lib --libdir=/usr/lib --enable-shared --with-system-zlib --enable-long-long --enable-nls --without-included-gettext --disable-libssp --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=avr Thread model: single gcc version 4.8.1 (GCC) But the bug also occurs using: Using built-in specs. Reading specs from /home/matthijs/pkg-x86_64-unknown-linux-gnu/bin/../lib/gcc/avr/5.1.0/device-specs/specs-avr2 COLLECT_GCC=/home/matthijs/pkg-x86_64-unknown-linux-gnu/bin/avr-gcc COLLECT_LTO_WRAPPER=/home/matthijs/pkg-x86_64-unknown-linux-gnu/bin/../libexec/gcc/avr/5.1.0/lto-wrapper Target: avr Configured with: /home/admin/avr-gcc-5.1.0/gcc-5.1.0/configure --disable-install-libiberty --disable-libssp --disable-libstdcxx-pch --disable-libunwind-exceptions --disable-nls --enable-fixed-point --enable-long-long --disable-werror --disable-__cxa_atexit --enable-checking=release --enable-clocale=gnu --enable-cloog-backend=isl --enable-gnu-unique-object --with-avrlibc=yes --with-dwarf2 --enable-languages=c,c++ --disable-libada --disable-doc --enable-lto --enable-gold --disable-plugin --prefix=/home/admin/avr-gcc-5.1.0/pkg-x86_64-unknown-linux-gnu/ --disable-shared --with-gnu-ld --host=x86_64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu --target=avr Thread model: single gcc version 5.1.0 (GCC)