Search code examples
c++avr

GCC produces unneccessary register pushes for simple ISR on AVR


I have some simple C++ programm that produces the following assembler text if compile with g++. The only statement is sbi, which doesn't affect any status flags. I wonder why G++ produces these useless push/pop's of r0 and r1?

.global __vector_14
        .type   __vector_14, @function
__vector_14:
        push r1  ; 
        push r0  ; 
        in r0,__SREG__   ; ,
        push r0  ; 
        clr __zero_reg__         ; 
/* prologue: Signal */
/* frame size = 0 */
/* stack size = 3 */
.L__stack_usage = 3
        sbi 0x1e,0       ; ,
/* epilogue start */
        pop r0   ; 
        out __SREG__,r0  ; ,
        pop r0   ; 
        pop r1   ; 
        reti
        .size   __vector_14, .-__vector_14

Is there any way that g++ automatically omits these register saves. I don't want to declare the ISR as ISR_NAKED in general.

Edit: This is the correcponding C++ code (-Os or -O3):

#include <avr/interrupt.h>

struct AppFlags final {
    bool expired : 1;
} __attribute__((packed));

int main() {
}

ISR(TIMER0_COMPA_vect) {
    auto f = reinterpret_cast<volatile AppFlags*>(0x3e);
    f->expired = true;
}

Solution

  • Simple answer:

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=20296

    The difficulty is, that the present architecture of the avr back-end does not easily permit to improve this case: Every instruction pattern (like "multiply two 16 bit integers" or "sign-extend a 16 bit variable to 32 bits") presently is free to assume that may overwrite or change r0 and r1 unless it leaves the "zero_reg" with 0 after finishing it's task.

    Resolving this issue, IMHO, would require a major refactoring of the back-end.

    This is a long standing bug / enhancement request to avr-backend.