GCC 为 AVR 上的简单 ISR 生成不必要的寄存器推送
GCC produces unneccessary register pushes for simple ISR on AVR
我有一些简单的 C++ 程序,如果使用 g++ 编译,它会生成以下汇编程序文本。唯一的语句是 sbi
,它不影响任何状态标志。我想知道为什么 G++ 会产生这些无用的 r0
和 r1
的 push/pop?
.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
有没有办法让 g++ 自动忽略这些寄存器保存。我一般不想将 ISR 声明为 ISR_NAKED
。
编辑:
这是相应的 C++ 代码(-Os 或 -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;
}
GCC 推送所有使用的寄存器。您唯一真正的办法是启用 naked
属性,该属性只会推送堆栈指针。或者换成汇编语言。
简单回答:
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.
这是对 avr-backend 的一个长期存在的错误/增强请求。
原因是您使用的是过时的编译器。 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.
版本 8 之前的 GCC 没有优化 ISR 序言和结尾。
GCC 8 and later now emit __gcc_isr
pseudo-instructions 在使用某些优化级别(例如 -Os
或提供 -mgas-isr-prologues
.
进行编译时包含您的 ISR 主体
GNU 汇编程序(来自不太过时的 binutils 版本)理解这些伪指令和 scans the instructions (via) 在 __gcc_isr 1
和 __gcc_isr 2
之间决定哪个 r0(tmp 寄存器), r1(零寄存器),SREG(状态寄存器)需要保存和恢复。
因此,对于您的示例,我得到了一个非常小的 objdump(使用 GCC 11.1 编译时):
$ avr-objdump -d foo.o
[..]
00000000 <__vector_14>:
0: f0 9a sbi 0x1e, 0 ; 30
2: 18 95 reti
[..]
当我告诉 GCC 只发出程序集时,我们看到了伪指令:
$ 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
[..]
我有一些简单的 C++ 程序,如果使用 g++ 编译,它会生成以下汇编程序文本。唯一的语句是 sbi
,它不影响任何状态标志。我想知道为什么 G++ 会产生这些无用的 r0
和 r1
的 push/pop?
.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
有没有办法让 g++ 自动忽略这些寄存器保存。我一般不想将 ISR 声明为 ISR_NAKED
。
编辑: 这是相应的 C++ 代码(-Os 或 -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;
}
GCC 推送所有使用的寄存器。您唯一真正的办法是启用 naked
属性,该属性只会推送堆栈指针。或者换成汇编语言。
简单回答:
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.
这是对 avr-backend 的一个长期存在的错误/增强请求。
原因是您使用的是过时的编译器。 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.
版本 8 之前的 GCC 没有优化 ISR 序言和结尾。
GCC 8 and later now emit __gcc_isr
pseudo-instructions 在使用某些优化级别(例如 -Os
或提供 -mgas-isr-prologues
.
GNU 汇编程序(来自不太过时的 binutils 版本)理解这些伪指令和 scans the instructions (via) 在 __gcc_isr 1
和 __gcc_isr 2
之间决定哪个 r0(tmp 寄存器), r1(零寄存器),SREG(状态寄存器)需要保存和恢复。
因此,对于您的示例,我得到了一个非常小的 objdump(使用 GCC 11.1 编译时):
$ avr-objdump -d foo.o
[..]
00000000 <__vector_14>:
0: f0 9a sbi 0x1e, 0 ; 30
2: 18 95 reti
[..]
当我告诉 GCC 只发出程序集时,我们看到了伪指令:
$ 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
[..]