为什么';gcc处理易失性寄存器?

为什么';gcc处理易失性寄存器?,gcc,compiler-optimization,volatile,avr,cpu-registers,Gcc,Compiler Optimization,Volatile,Avr,Cpu Registers,我正在为AVR平台做一个定时循环,在那里我在一个ISR中倒计时一个字节。由于此任务是我的程序的主要功能,我想永久保留一个处理器寄存器,以便ISR在其通常的代码路径为递减、比较为零和reti时不必遇到内存障碍 演示如何将变量绑定到寄存器,这样我就可以毫无问题地工作了。但是,由于此变量在主程序(用于启动计时器倒计时)和ISR(用于实际计数和信号完成)之间共享,因此它也应该是易失的,以确保编译器在优化它时不会做得太聪明 在这种情况下(在整个单片构建中保留一个寄存器),组合volatile regist

我正在为AVR平台做一个定时循环,在那里我在一个ISR中倒计时一个字节。由于此任务是我的程序的主要功能,我想永久保留一个处理器寄存器,以便ISR在其通常的代码路径为递减、比较为零和
reti
时不必遇到内存障碍

演示如何将变量绑定到寄存器,这样我就可以毫无问题地工作了。但是,由于此变量在主程序(用于启动计时器倒计时)和ISR(用于实际计数和信号完成)之间共享,因此它也应该是易失的,以确保编译器在优化它时不会做得太聪明

在这种情况下(在整个单片构建中保留一个寄存器),组合
volatile register
在语义上是有意义的,因为“将此变量永久存储在register
rX
中,但不要优化掉检查,因为寄存器可能会被外部修改”。但是,GCC不喜欢这样,它可能会继续优化变量访问


GCC中的错误历史表明编译器团队根本不愿意考虑我所描述的场景类型,认为提供它是毫无意义的。我是否遗漏了

易失性寄存器
方法本身是个坏主意的一些基本原因,或者这是一种语义上有意义的情况,但编译器团队对处理不感兴趣?

对于编译器的代码生成器来说,为一个完整的编译单元保留一个变量的寄存器可能限制太多。也就是说,每个C例程都必须不使用该寄存器

当代码超出范围时,如何保证其他被调用的例程不使用该寄存器?即使是像串行i/o例程这样的东西也不能使用保留寄存器。编译器不会根据用户程序中的数据定义重新编译其运行时库

您的应用程序是否真的对时间如此敏感,以至于可以检测到从L2或L3启动内存的额外延迟?如果是这样,那么您的ISR可能运行得非常频繁,以至于所需的内存位置始终可用(即,它不会通过缓存被调回),因此不会遇到内存障碍(我假设内存障碍指的是cpu中的内存如何通过缓存等实际运行)。但要实现这一点,up必须拥有相当大的一级缓存,ISR必须以非常高的频率运行


最后,有时应用程序的需求使得有必要在ASM中对其进行编码,在这种情况下,您可以完全按照自己的要求执行

对于编译器的代码生成器来说,为一个完整的编译单元保留一个变量的寄存器可能限制太多。也就是说,每个C例程都必须不使用该寄存器

当代码超出范围时,如何保证其他被调用的例程不使用该寄存器?即使是像串行i/o例程这样的东西也不能使用保留寄存器。编译器不会根据用户程序中的数据定义重新编译其运行时库

您的应用程序是否真的对时间如此敏感,以至于可以检测到从L2或L3启动内存的额外延迟?如果是这样,那么您的ISR可能运行得非常频繁,以至于所需的内存位置始终可用(即,它不会通过缓存被调回),因此不会遇到内存障碍(我假设内存障碍指的是cpu中的内存如何通过缓存等实际运行)。但要实现这一点,up必须拥有相当大的一级缓存,ISR必须以非常高的频率运行


最后,有时应用程序的需求使得有必要在ASM中对其进行编码,在这种情况下,您可以完全按照自己的要求执行

对AVR变量使用
volatile
关键字的原因是,正如您所说,避免编译器优化对变量的访问。现在的问题是,这是如何发生的

变量有两个可以驻留的位置。在通用寄存器文件中或RAM中的某个位置。考虑变量驻留在RAM中的情况。为了访问变量的最新值,编译器使用某种形式的
ld
指令(例如
ldsr16,0x000f
)从RAM加载变量。在这种情况下,变量存储在RAM位置
0x000f
,程序在
r16
中复制了该变量。现在,如果中断被启用,事情就会变得有趣。假设加载变量后,发生以下情况
inc r16
,然后触发中断并运行相应的ISR。在ISR中,也使用变量。然而,有一个问题。该变量有两种不同的版本,一种在RAM中,另一种在r16中。理想情况下,编译器应该使用
r16
中的版本,但是这个版本不能保证存在,因此它从RAM加载它,而现在,代码不能按需要运行。然后输入
volatile
关键字。变量仍然存储在RAM中,但是,编译器必须确保变量在发生任何其他事情之前在RAM中更新,因此可能会生成以下程序集:

cli
lds r16, 0x000f
inc r16
sei
sts 0x000f, r16
首先,中断被禁用。然后,将变量加载到r16中。增加变量,启用中断,然后存储变量。在变量存储回RAM之前启用全局中断标志可能会让人感到困惑,但从指令集手册:

SEI后面的指令将在任何挂起的中断之前执行

这意味着
sts
指令将在任何中断之前执行
uint8_t var;

volatile uint8_t volVar;

register uint8_t regVar asm("r13");

#define NOP asm volatile ("nop\r\n":::)

int main(void)
{
    var = 1; // <-- kept
    if ( var == 0 ) {
        NOP; // <-- optimized away, var is not volatile
    }

    volVar = 1; // <-- kept
    if ( volVar == 0 ) {
        NOP; // <-- kept, volVar *is* volatile
    }

    regVar = 1; // <-- optimized away, regVar is treated like a local variable
    if ( regVar == 0 ) {
        NOP; // <-- optimized away consequently
    }

    for(;;){}
}