GCC在ISR中生成无用的代码

use*_*847 6 c gcc avr interrupt

我有一个非常简单的中断服务程序(ISR)为atmega328编写,并使用AVR工作室使用avrgcc(使用-Os)编译.

ISR (TIMER0_OVF_vect) { 
    txofcnt++;  //count overflows and store in uint16_t 
}
Run Code Online (Sandbox Code Playgroud)

如果你注意到生成的程序集(如下),它使用r24,r25来获取增加易失性uint16_t txofcnt的作业,但它也是push-write-pop r1,r28,r29而没有读取它们.它还有一个额外的r0推/弹,而不会在它们之间使用它.

我不知道为什么r1被推,清除然后最终poped.但是为什么gcc觉得需要将EIMSK和GPIOR0加载到寄存器中然后不使用它们.如果你能告诉我GPIOR0的用途是什么,那么数据表说它存在但没有描述.

00000258 <__vector_16>:

ISR (TIMER0_OVF_vect) {
 258:   1f 92           push    r1
 25a:   0f 92           push    r0
 25c:   00 90 5f 00     lds r0, 0x005F
 260:   0f 92           push    r0
 262:   11 24           eor r1, r1
 264:   8f 93           push    r24
 266:   9f 93           push    r25
 268:   cf 93           push    r28
 26a:   df 93           push    r29
 26c:   cd b7           in  r28, 0x3d   ; 61 reads register EIMSK
 26e:   de b7           in  r29, 0x3e   ; 62 reads register GPIOR0
    txofcnt++;  
 270:   80 91 0a 01     lds r24, 0x010A
 274:   90 91 0b 01     lds r25, 0x010B
 278:   01 96           adiw    r24, 0x01   ; 1
 27a:   90 93 0b 01     sts 0x010B, r25
 27e:   80 93 0a 01     sts 0x010A, r24
}
 282:   df 91           pop r29
 284:   cf 91           pop r28
 286:   9f 91           pop r25
 288:   8f 91           pop r24
 28a:   0f 90           pop r0
 28c:   00 92 5f 00     sts 0x005F, r0
 290:   0f 90           pop r0
 292:   1f 90           pop r1
 294:   18 95           reti
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 4

https://gcc.gnu.org/wiki/avr-gcc有一些关于 AVR 的 GCC 寄存器用法的文档

\n\n

与您的问题相关的一些段落:

\n\n
\n

固定寄存器

\n\n

固定寄存器是不会由 GCC 寄存器分配器分配的寄存器。寄存器 R0 和 R1 是固定的,并在打印汇编指令时隐式使用\n:

\n\n
    \n
  • 罗0

    \n\n

    用作暂存寄存器,使用后无需恢复。它必须在中断服务程序(ISR)的序言和尾声中保存和恢复。在内联汇编器中,您可以使用 \n __tmp_reg__作为暂存寄存器。

  • \n
  • R1

    \n\n

    始终包含零。在insn期间,内容可能被破坏,例如,通过使用R0/R1作为隐式输出寄存器的MUL指令。如果insn破坏了R1,则insn必须随后将R1恢复为零。该寄存器必须保存在 ISR 序言中,并且必须\n 然后设置为零,因为 R1 可能包含非零的值。\n ISR 结尾会恢复该值。在内联汇编器中,您可以使用 \n __zero_reg__作为零寄存器。

    \n\n

    ...

  • \n
\n\n

调用使用的寄存器

\n\n

调用使用或调用破坏的通用寄存器 (GPR) 是可能被函数调用破坏(破坏)的寄存器。

\n\n
    \n
  • R18\xe2\x80\x93R27、R30、R31

    \n\n

    这些探地雷达被呼叫破坏了。普通函数可以使用它们而无需恢复内容。中断服务例程 (ISR) 必须保存和恢复它们使用的每个寄存器。

    \n\n

    ...

  • \n
\n\n

调用保存的寄存器

\n\n
    \n
  • R2\xe2\x80\x93R17、R28、R29

    \n\n

    其余的 GPR 是调用保存的,即使用此类寄存器的函数必须恢复其原始内容。即使寄存器用于传递函数参数,这也适用。

  • \n
\n
\n\n

以下是我对编译器为何在 ISR 序言/尾声中执行一些明显不必要的寄存器保存/恢复的猜测:

\n\n
    \n
  • r0r1被保存/恢复,因为编译器生成或调用的代码将做出上面概述的假设。由于它们不被 GCC 的寄存器分配器跟踪,因此序言必须确保它们被保存(并且在r1这种情况下初始化为 0)。

  • \n
  • r28r29用于保存堆栈指针(0x3d/SPL0x3e/ SPH)。我猜测(我想强调这是一个猜测)编译器编写者假设中断处理程序交换堆栈可能很常见,这确保了 ISR 可以恢复正在使用的堆栈当中断发生时。编译器可以假设这些寄存器不会被调用的函数更改,因为它们是“调用保存”寄存器。

  • \n
\n\n

另外,您应该注意,显然“额外”的压入和弹出r0是将状态寄存器保存SREG在堆栈上。即使在这些指令和指令r0之间没有使用,请记住该寄存器是一个暂存寄存器,不会被寄存器分配器跟踪,因此编译器不会假设它在加载到其中后不会发生变化。pushpopr0r0SREG

\n\n

附带说明一下,0x3d和的读取0x3eSPLSPH堆栈指针寄存器,而不是EIMSKGPIOR0寄存器。有关使用/指令而不是加载或存储指令时寄存器寻址有何不同的详细信息,请参阅此处参考手册第 625 页寄存器摘要表的注释 4 。INOUT

\n\n

对于以下方面的奖励积分GPIOR0

\n\n
\n

8.5.1 通用 I/O 寄存器

\n\n

ATmega48A/PA/88A/PA/168A/PA/328/P 包含三个通用 I/O 寄存器。这些寄存器可用于存储任何信息,并且对于存储全局变量和状态标志特别有用。地址范围 0x00 - 0x1F 内的通用 I/O 寄存器可使用 SBI、CBI、SBIS 和 SBIC 指令直接进行位访问。

\n
\n