傻孩子(GorgonMeducer) · 2020年06月10日

【编译器玄学研究报告】第二期——break

作者:GorgonMeducer 傻孩子
首发:裸机思维

image.png

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

image.png

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

image.png

3、插入以下的测试代码

#include <stdio.h>

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 this
leave switch...

然而,我们实际观测到的是:

image.png

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

image.png

可以发现,代码中 do{}while() 结构内部的break被错误的当作了switch的break,从而跳过了 do{}while() 结构后面的代码。通过修改优化等级到-O3 -Osize或者是-O2 -Otime都可以避开这一问题。那么,这一问题是否严重呢?我觉得我要手动@一下所有使用protoThread和所有使用switch状态机的童鞋们,就问你们慌不慌image.png

【玄学说法】“状态机好像容易跑飞……我也不知道是怎么回事,代码逻辑没问题啊?”;“高优先级下生成的代码不可靠!”;“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的怀抱……

专栏推荐文章

【编译器玄学研究报告】第一期——位域和volatile
大白话说嵌入式安全(1)
大白话说嵌入式安全(2)
什么是嵌入式系统(上)
什么是嵌入式系统(中)
什么是嵌入式(下)—— “重力”和“沉淀”

如果你喜欢我的思维,欢迎订阅裸机思维
版权归裸机思维(傻孩子图书工作室旗下公众号)所有,
所有内容原创,严禁任何形式的转载。
推荐阅读
关注数
1466
内容数
108
探讨嵌入式系统开发的相关思维、方法、技巧。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息