【转】C 编译器优化过程中的 Bug
C 编译器优化过程中的 Bug
一个朋友向我指出一个最近他们发现的 GCC 编译器优化过程(加上 -O3 选项)里的 bug,导致他们的产品出现非常诡异的行为。这使我想起以前见过的一个 GCC bug。当时很多人死活认为那种做法是正确的,跟他们说不清楚。简言之,这种有问题的优化,喜欢利用 C 语言的“未定义行为”(undefined behavior)进行推断,最后得到奇怪的结果。
这类优化过程的推理方式都很类似,他们使用一种看似严密而巧妙的推理,例如:“现在有一个整数 x,我们不知道它是多少。但 x 出现在一个条件语句里面,如果 x > 1,那么程序会进入未定义行为,所以我们可以断定 x 的值必然小于或者等于 1,所以现在我们利用 x ≤ 1 这个事实来对相关代码进行优化……”
看似合理,然而它却是不正确的,你能看出来这样的推理错在何处吗?我一时想不起来之前具体的例子了(如果你知道的话告诉我)。上网搜了一下相关话题,发现这篇 Chris Lattner (LLVM 和 Swift 语言 的设计者) 写于 2011 年的文章。文中指出,编译器利用 C 语言的“未定义行为”进行优化,是合理的,对于性能是很重要的,并且举出这样一个例子:
void contains_null_check(int *P) {
int dead = *P;
if (P == 0)
return;
*P = 4;
}
这例子跟我之前看到的 GCC bug 不大一样,但大致是类似的推理方式:这个函数依次经过这样两个优化步骤(RNCE 和 DCE),之后得出“等价”的代码:
void contains_null_check_after_RNCE(int *P) {
int dead = *P;
if (false) // P 在上一行被访问,所以这里 P 不可能是 null
return;
*P = 4;
}
void contains_null_check_after_RNCE_and_DCE(int *P) {
//int dead = *P; // 死代码消除
//if (false) // 死代码
// return; // 死代码
*P = 4;
}
他的推理方式是这样:
- 首先,因为在
int dead = *P里面,指针P的地址被访问,如果程序顺利通过了这一行而没有出现未定义行为(比如当掉),那么之后P就不可能是 null,所以我们可以把P == 0优化为false。 - 因为条件是
false,所以整个 if 语句都是死代码,被删掉。 dead变量赋值之后,没有被任何其它代码使用,所以对dead的赋值是死代码,可以消去。
最后函数就只剩下一行代码 *P = 4。然而经我分析,发现这个优化转换是根本错误的做法(unsound 的变换),而不只是像他说的“存在安全隐患”。现在我来考考你,你知道这为什么是错的吗?值得庆幸的是,现在如果你把这代码输入到 Clang,就算加上 -O3 选项,它也不会给你进行这个优化。这也许说明 Lattner 的这个想法后来已经被 LLVM 团队抛弃。
我写这篇文章的目的其实是想告诉你,不要盲目的相信编译器的作者们做出的变换都是正确的,无论它看起来多么的合理,只要打开优化之后你的程序出现奇葩的行为,你就不能排除编译器进行了错误优化的可能性。Lattner 指出这样的优化完全符合 C 语言的标准,这说明就算你符合国际标准,也有可能其实是错的。有时候,你是得相信自己的直觉……
【转】C 编译器优化过程中的 Bug的更多相关文章
- VS编译器优化诱发一个的Bug
VS编译器优化诱发一个的Bug Bug的背景 我正在把某个C++下的驱动程序移植到C下,前几天发生了一个比较诡异的问题. 驱动程序有一个bug,但是这个bug只能 Win32 Release 版本下的 ...
- SQL优化过程中常见Oracle HINT
在SQL语句优化过程中,我们经常会用到hint,现总结一下在SQL优化过程中常见Oracle HINT的用法: 1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量, ...
- mysql优化过程中遇见的坑(mysql优化问题特别注意)
不要听信你看到的关于优化的“绝对真理”,包括本文所讨论的内容,而应该是在实际的业务场景下通过测试来验证你关于执行计划以及响应时间的假设. 单条查询最后添加 LIMIT 1,停止全表扫描. 对于char ...
- TensorRT优化过程中的dropout问题
使用tensorRT之前,你一定要注意你的网络结构是否能够得到trt的支持,无论是CNN还是RNN都会有trt的操作. 例如:tf.nn.dropout(features, keep_prob),tr ...
- 【填坑往事】Android手机锁屏人脸解锁优化过程实录
背景 写这篇文章,主要是为了以后面试方便.因为我简历上写了,上一份工作的最大亮点是将人脸解锁的速度由1200ms优化到了600ms,所以这些内容已经回答无数遍了.但每次总觉得回答的不完整,或者说总感觉 ...
- C#编译器优化那点事 c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错 webAPI 控制器(Controller)太多怎么办? .NET MVC项目设置包含Areas中的页面为默认启动页 (五)Net Core使用静态文件 学习ASP.NET Core Razor 编程系列八——并发处理
C#编译器优化那点事 使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的.优化代码 ...
- Bug,项目过程中的重要数据
作者|孙敏 为什么要做Bug分析? Bug是项目过程中的一个有价值的虫子,它不只是给开发的,而是开给整个项目组的. 通过Bug我们能获得什么? 积累测试方法,增强QA的测试能力,提升产品质量 发现项目 ...
- Coding过程中遇到的一些bug
1. 在使用layoutSubviews方法调整自定义view内部的子控件坐标时,最好不要使用子控件的centerX,centerY属性,否则会出现奇怪的bug. 如果一定要用,务必仔细检查,该子控件 ...
- Visual C++中的编译器优化
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:Visual C++中的编译器优化.
随机推荐
- spring结合mybatis实现数据库读写分离
随着系统用户访问量的不断增加,数据库的频繁访问将成为我们系统的一大瓶颈之一.由于项目前期用户量不大,我们实现单一的数据库就能完成.但是后期单一的数据库根本无法支撑庞大的项目去访问数据库,那么如何解决这 ...
- C#.NET常见问题(FAQ)-如何让文本框textbox内容限制为数字
//限制文本框的输入 private void txtQuestionScore_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyCha ...
- 使用Filter过滤非法内容
1.首先,需要编写一个响应的封装器ResponseReplaceWrapper,用它来缓存response中的内容,代码如下: ResponseReplaceWrapper.java package ...
- iOS 动画效果。简单的提示消失
UILabel * label1 = [[UILabel alloc]initWithFrame:CGRectMake(, , , )]; label1.text = @"qingjoin& ...
- JavaWeb之tomcat安装、配置与使用(一)
一.Tomcat下载与安装: 1.直接到官网下载Tomcat安装程序包:http://tomcat.apache.org/ 2.下载下来后是个压缩包,如:apache-tomcat-7.0.40.zi ...
- thinkPHP 快速上手
http://www.kancloud.cn/manual/thinkphp5/118006 克隆项目 //首先克隆下载应用项目仓库 git clone https://github.com/top- ...
- [Android]通过alias映射所有Busybox命令
# 把Busybox所有的命令通过alias映射出来(若此命令不存在时) # 这么做只是为了平时敲命令行时无须额外的打 busybox (感觉很麻烦的说) for applet in $(busybo ...
- Software development --daily scrum team
History[edit] Scrum was first defined as "a flexible, holistic product development strategy whe ...
- 7、redis之使用spring集成commons-pool来操作常见数据类型
环境的搭建参见:http://www.cnblogs.com/yangzhilong/p/4729857.html 下面直接贴具体的测试代码: package com.yzl; import java ...
- Dubbo整合SpringBoot
目前的dubbo已支持和springboot集成,还是之前的例子,这次我们通过springboot容器来实现.借此了解一下基于springboot容器启动的dubbo的配置及使用. 1. 准备工作 创 ...