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

【问题背景】

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

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

【问题现象】

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

代码1

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

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

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

【问题根因】

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

 

代码2

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

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

 

代码3

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

【解决方案】

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

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

unsigned int volatile *addr = 0xf0000000;

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

 

代码4

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

【未解决问题】

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

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值