【问题标题】:How to instruct avr-gcc to optimize volatile variables?如何指示 avr-gcc 优化 volatile 变量?
【发布时间】:2018-11-09 05:37:15
【问题描述】:

中断服务处理程序代码:

volatile unsigned char x = 0;
void interruptHandler() __attribute__ ((signal));
void interruptHandler() {
    f();
    g();
}

来电:

void f() { x ++; } // could be more complex, could also be in a different file
void g() { x ++; } // as `f()`, this is just a very simple example

因为x是一个volatile变量,所以每次使用都会读写。中断处理程序的主体编译为 (avr-gcc -g -c -Wa,-alh -mmcu=atmega328p -Ofast file.c):

lds r24,x
subi r24,lo8(-(1))
sts x,r24
lds r24,x
subi r24,lo8(-(1))
sts x,r24

现在我可以手动内联函数并使用临时变量:

unsigned char y = x;
y ++;
y ++;
x = y;

或者我可以写:

x += 2;

这两个例子都编译得更高效:

lds r24,x
subi r24,lo8(-(2))
sts x,r24

是否可以告诉 avr-gcc 优化对 interruptHandler 内部的 volatile 变量的访问,即自动进行手动优化?

毕竟interruptHandler在运行的同时,全局中断被禁用,x不可能改变。我不喜欢手动优化代码,因此可能会创建重复代码(如果在其他地方需要 f()g())并引入错误。

【问题讨论】:

  • 我可能在这里遗漏了一些东西,但如果 x 没有改变,你为什么不做x += 2
  • 在临时变量y 中计算x 的新值有什么丑陋和困惑?只需添加一条注释,说明您正在使用临时变量 y 以防止多次读取 volatile 变量 x
  • 我也遇到过类似的情况。我有一个定时器中断例程,它增加全局滴答计数,声明为易失性。该变量还在 ISR 内部用于其他目的。在我引入滴答计数的本地副本之前,编译器发出了低效的代码。我不知道具有变量的易失性和非易失性版本的联合是否可以工作,但会使代码不可读。使用本地副本,让编译器完成优化工作。
  • 编译器正在以优化的方式执行您告诉它的操作。您拥有的两个示例不一样,编译器也不能对它们进行相同的解释。如果您想要这样的优化,您需要像您所展示的那样自己做,而不是编译器可以根据您使用的语言做的事情。如果你想要 volatile,你会得到 volatile(希望如此)如果你不想要 volatile 行为,那么不要将其声明为 volatile,以其他方式解决它。
  • @kkrambo 我在示例中略微增加了复杂性。即使中断处理程序中的代码编译成几十条指令,它也可能分布在不同的函数中以提高可读性。在这种情况下,很难使用临时变量。而且,除此之外,我更喜欢编译器自己做一些事情。

标签: gcc optimization microcontroller volatile avr-gcc


【解决方案1】:

是否可以告诉 avr-gcc 优化对 interruptHandler 内部 volatile 变量的访问,即自动进行手动优化?

不,这在 C 语言中是不可能的。

毕竟,在 interruptHandler 运行时,全局中断被禁用

编译器知道这一点 - 您可以简单地将sei 放入处理程序以重新打开它们。

还要注意,硬件寄存器也声明为volatile。其中一些——比如UART数据寄存器——即使在读取时也会产生副作用。编译器不得删除对这些的任何读取或写入。

【讨论】:

  • 请注意,我特别询问了avr-gcc,它确实具有启用非标准兼容优化的功能属性和命令行选项。所以我希望我可以明确地告诉编译器:我自担风险,我在此指示您忽略interruptHandler() 中变量访问的volatile 关键字!
  • 硬件寄存器访问必须完全按照说明进行 - 特别是在中断处理程序中 - 这些将不再适合您,因为编译器也会“优化”这些。更改 f()g() 函数比您建议的解决方案实现起来简单。
【解决方案2】:

如果您将变量声明为易失性,那么对其的所有访问都是易失性的 - 编译器将按照源代码所述的次数对其进行读写,而无需将它们组合或进行类似的优化。

因此,如果您想组合优化,请声明不带“volatile”的变量 - 然后您将在中断代码中获得所需的内容。

然后从中断代码外部,您可以使用类似以下宏的内容强制进行易失性访问:

#define volatileAccess(v) *((volatile typeof((v)) *) &(v))

在中断代码之外使用“volatileAccess(x)”而不是“x”。

别忘了“volatile”并不意味着“atomic”!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2016-01-11
    相关资源
    最近更新 更多