【编译器玄学研究报告】第二期——break
【说在前面的话】
编译器玄学报告第一期出来后,并没有任何人理睬我在文章一开头留下的问题,相反大部分人都是来围观“闰年更新”的……更有甚者感慨:我娃都出生了,这个公众号终于更新了。真的非常非常抱歉,感觉辜负了很多人的期待;同时也非常非常的感谢,感谢你们的耐心和信任——为了纪念这一时刻,我准备就大家非常信赖和依恋的armcc(Arm Compiler 5)爆个黑料。让我们来围观一个如假包换的编译器bug。
【正文】
Arm Compiler 5,也就是大家熟悉的armcc,距离官方2017年最后一次更新已经过去3年多了。实际上官方早在2016年发布Arm Compiler 5.06u3的时候就已经通过各种渠道对外界喊话——“我要是再更新armcc,我就卖了我自己”,结果2017年,5.06u6不期而至,孙正义爸爸的钱还是“真香”的——这里也许有些许戏虐的成分在里面,但armcc接近软件生命周期的终点是不争的事实:
armcc 真的太老了!也许用户用起来感觉还挺趁手,但实际上已经是“屎山”一座了;
今天,我们就给大家展示一个Arm Compiler 5.06u6 “最终绝对不改版6” 中一个惊为天人的BUG。
1、准备好一个新鲜的 Arm Compiler 5.06u6。如果不确定自己的armcc是不是这一版本,可以打开MDK的help->About,如果你是日常的armcc玩家,应该可以看到类似如下的信息(是的,就是这个 V5.06 update 6)

2、随便建立一个可以编译和调试的例子工程,并将优化等级设置为 -Otime -O3。具体方法如图所示:

3、插入以下的测试代码
void break_tesk(uint_fast8_t chValue){ printf("entering switch...\r\n"); switch (chValue >> 4) { case 0: printf("do something..."); break; case 1: printf("do something..."); break; case 2: do { printf("\tInput is 0x%02x", chValue); if ((chValue & 0x0F) < 7) { break; } printf("You will not see this"); } while(0); printf("\tYou should see this "); break; default: printf("it is a default"); break; } printf("\r\nleave switch...\r\n ");}4、在超级循环中调用该函数,并传入参数0x22,例如:
void main(void){ ... break_test(0x22); ...}5、编译并运行,观察输出:
分析代码不难发现,针对输入0x22,我们会从do{}while(0); 结构中跳出,并继续执行后续的代码,也就是打印“\tYou should see this ”,因此一个可能的输出结果是:
entering switch... Input is 0x22 You should see thisleave switch...然而,我们实际观测到的是:

6、修改优化等级为 -O3 -Osize(如图所示,只要去掉 Optimize for time前面的勾选就行了),编译并运行,观察输出:

可以发现,代码中 do{}while() 结构内部的break被错误的当作了switch的break,从而跳过了 do{}while() 结构后面的代码。通过修改优化等级到-O3 -Osize或者是-O2 -Otime都可以避开这一问题。那么,这一问题是否严重呢?我觉得我要手动@一下所有使用protoThread和所有使用switch状态机的童鞋们,就问你们慌不慌。
【玄学说法】“状态机好像容易跑飞……我也不知道是怎么回事,代码逻辑没问题啊?”;“高优先级下生成的代码不可靠!”;“switch里面的break和for以及do while的break究竟怎么用?我好方”;“我遇到一个bug,你来看看”,“我什么都没改啊,换个优化等级就对了……”,
【实际情况】编译器bug没的洗……但是bug就是bug,不要怀疑人生,不要怀疑自己所学的语法,发现bug有条件的话请及时报告
【后记】
其实按道理说,这种bug应该非常明显,不至于被留到最后一个版本,仔细想想,至少有以下几种可能:
这是 Arm Compiler 5.06u6 从 5.06u5 升级过程中引入的——正可谓拔出萝卜带出泥。可惜实际情况并不是这样的。有兴趣的朋友们可以去测试下老版本;
大部分用户对C语法掌握情况不佳,基本处于模棱两可的状况;遇到类似情况不会用一个严谨的态度首先去确认正确的语法及行为;在坚持正确语法的情况下分析问题,从而判断出这是一个编译器错误;
大部分用户不敢用最高的-O3 -Otime优化;
大部分开最高优化的用户关心的主要是代码尺寸,从而恰好避开了这个问题;
大部分用户没空去纠结这一问题;
大部分用户遇到这类问题后,默默的选了-O0……
大部分用户相信玄学……
少部分牛人发现问题后懒得报告……
“大人,时代变了”
最后,欢迎大家尽早投入到Arm Compiler 6、IAR、GCC的怀抱……





