[Bug target/77326] New: [avr] Invalid optimization using varargs and a weak function

matthijs at stdin dot nl gcc-bugzilla@gcc.gnu.org
Mon Aug 22 18:50:00 GMT 2016


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)


More information about the Gcc-bugs mailing list