关于O2编译选项的一个过优化问题及其解决方法

【问题背景】

O2编译选项广泛用于嵌入式编程中,可大幅降低CPU耗时。

但优化编译选项级别越高,编译工具链对源代码的指令优化与重排力度越大,最后生成的汇编指令越有可能与编程者预期的逻辑有偏差。

【问题现象】

目前在实际工作中发现O2下循环语句工作异常。实验代码段如下:

关于O2编译选项的一个过优化问题及其解决方法_第1张图片

代码1

代码1实现的功能是:轮询地址为0xf0000000的数值,一旦数值为1则跳出循环继续进行“其他处理”。

在异构编程中这种写法经常被用于等待某一内存/寄存器的数值发生变化。

但实践发现,上述代码经过O2编译后,实际工作现象为:即使0xf0000000地址内数值为1,也无法跳出循环。这与编程预期严重不符。

【问题根因】

代码1经过O2优化后的汇编文件为:

关于O2编译选项的一个过优化问题及其解决方法_第2张图片

 

代码2

从代码2可见,从地址0xf0000000中读取数值的动作只发生了一次,而在循环中未再读取,只是单纯地将第一次读取到的数值与立即数进行比对。故即使0xf0000000地址中的数值变化为了1,也无法跳出循环。

另外,通过去除优化选项作为对照组来确定问题根因为“启用了O2”,此时代码1的汇编文件为:

关于O2编译选项的一个过优化问题及其解决方法_第3张图片

 

代码3

从代码3可见,在循环中每次都先读取0xf0000000地址中的数值后再与立即数进行比对,故在不启用O2的情况下,汇编指令的逻辑与编程者预期一致。

【解决方案】

通过上述分析可知问题根因为:启用了O2。但不使用O2是不现实的,尤其是对于CPU不强的平台来讲。

面对这种问题,可以在全部代码处于O2优化的情况下,通过使用volatile关键字来保护某些代码,避免编译器对其进行优化,即将代码1中的第3行改为:

unsigned int volatile *addr = 0xf0000000;

此时代码1的汇编文件为:

关于O2编译选项的一个过优化问题及其解决方法_第4张图片

 

代码4

由代码4可见,在每次做比对之前,都会从0xf0000000中重新读取数值,符合预期逻辑。并且代码4比完全不使用优化编译选项的代码3更为精简,耗时更短。

【未解决问题】

目前在交叉编译工具链arm-seev100-linux-gnueabihf-和aarch64-none-linux-gnu-中均存在这个问题,但不确定使用gcc进行编译是否会存在这个问题,故暂不能完全确定是否和编译工具链有关。

你可能感兴趣的:(c,c语言,O2)