如何防止g++优化掉一个由IRQ可以更改的变量控制的循环?

22

考虑以下代码片段:

unsigned global;
while(global);

在一个由IRQ调用的函数中,修改了global变量。然而,g++编译器移除了“是否不为零”的测试,并将while循环转换成无限循环。

禁用编译器优化可以解决问题,但C++是否提供了一种语言构造来解决这个问题呢?


@Styne666:标题就是问题。 - Necrolis
请参见https://dev59.com/m2w05IYBdhLWcg3wsz3N。 - Samuel Harmer
@Styne666:你可以取消你的踩票。问题很清楚,尽管表述不够好。而且在这方面,禁用优化并不是一个真正的答案。 - Sebastian Mach
2
@Styne666:原标题包含“loop”,而不是“loops”。不要固执己见。重要的是现在问题是什么,不要管谁改变了它,现在你的原始动机已经不再成立。如果OP决定不喜欢这个微调,他有权再次编辑它,SO规则说,如果发生了编辑,你可以重新投票(信息:除非发生了编辑,否则不允许重新投票)。 - Sebastian Mach
@Styne666:我看到你已经删除了你的帖子,但是你的投票仍然存在。在这种行为中,我读到的是“我错了,但我的投票仍然作为惩罚,因为你敢批评我”。如果我错了,请纠正我,我会承认我方面的任何错误。对自己诚实。 - Sebastian Mach
显示剩余4条评论
3个回答

17

将变量声明为volatile

volatile unsigned global;

这个关键字告诉编译器 global 可以在不同的线程中修改,所有的优化都应该被关闭。


1
也许将“所有优化”更改为“某些优化”,因为仍然可以应用许多优化。例如,在global = 5 + 6;中,您的语句可能暗示着5 + 6没有被简化。 - Sebastian Mach
2
添加volatile限定符并不会改变所创建的操作码,它仍然会被翻译成一个无限循环... - 0xbadf00d
3
@luchian,循环不一定会停止,因为这是未定义的(数据竞争)。 - Johannes Schaub - litb
2
@JohannesSchaub-litb,你认为他应该怎么做呢? - Luchian Grigore
3
是的,这仍然是数据竞争。C++标准中没有将机器字长特殊对待的规定。C++当然可以支持那些无法以其本身字长进行原子写入或读取的CPU。 - David Schwartz
显示剩余10条评论

10

由于您正在使用GCC并且您说使变量volatile不起作用,因此您可以通过欺骗编译器来让优化器认为循环改变了变量:

while(global)
  asm volatile("" : "+g"(global));

这是一个内联汇编语句,它表示它修改了变量(作为输入输出操作数传递)。但它是空的,所以在运行时显然不会执行任何操作。尽管如此,优化器认为它修改了变量 - 程序员这么说,而编译器除了操作数替换(即简单地用另一个文本替换一个文本)之外,实际上并不关心内联汇编的主体,并且不会对其进行任何有趣的操作。

由于主体为空且使用的约束是可用的最通用约束,因此它应该可靠地在所有支持GCC内联汇编的平台上工作。


3
你可以在函数声明中使用GCC属性来按照每个函数的基础禁用优化:
void myfunc() __attribute__((optimize(0)));

请查看GCC函数属性页面获取更多信息。

如果变量必须位于特定的内存段中,请使用section变量属性将其正确放置。您可能还需要将其标记为volatile - Silas Parker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接