Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC produces unneccessary register pushes for simple ISR on AVR

Tags:

c++

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;
}
like image 651
wimalopaan Avatar asked Oct 28 '25 09:10

wimalopaan


2 Answers

The reason is that you are using an outdated compiler. The mentioned optimization has been added in v8 (released spring 2018), see GCC v8 Release Notes:

The compiler now generates efficient interrupt service routine (ISR) prologues and epilogues. This is achieved by using the new AVR pseudo instruction __gcc_isr which is supported and resolved by the GNU assembler.

like image 113
emacs drives me nuts Avatar answered Oct 31 '25 12:10

emacs drives me nuts


GCC before version 8 doesn't optimize ISR prologues and epilogues. GCC 8 and later now emit __gcc_isr pseudo-instructions that enclose your ISR body when compiling with some optimization levels such as -Os or when supplying -mgas-isr-prologues.

The GNU assembler (from not too outdated binutils versions) understands these pseudo-instructions and scans the instructions (via) between __gcc_isr 1 and __gcc_isr 2 to decide which of r0 (tmp register), r1 (zero register), SREG (status register) need to be saved and restored.

Thus, for your example I get a pretty minimal objdump (when compiling with GCC 11.1):

$ avr-objdump -d foo.o
[..]
00000000 <__vector_14>:
   0:   f0 9a           sbi 0x1e, 0 ; 30
   2:   18 95           reti
[..]

When I tell GCC to just emit assembly we see the pseudo-instructions:

$ avr-g++ -c -S -Os -mmcu=atmega328p  foo.c -fno-exceptions
$ cat foo.s
[..]
.global __vector_14
    .type   __vector_14, @function
__vector_14:
    __gcc_isr 1
/* prologue: Signal */
/* frame size = 0 */
/* stack size = 0...3 */
.L__stack_usage = 0 + __gcc_isr.n_pushed
    sbi 0x1e,0
/* epilogue start */
    __gcc_isr 2
    reti
    __gcc_isr 0,r0
[..]
like image 20
maxschlepzig Avatar answered Oct 31 '25 12:10

maxschlepzig



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!