乱序优化与GCC的bug
#include <stdio.h>
#include <stdlib.h> typedef unsigned short UINT16;
typedef unsigned int UINT32; struct EndPoint{
UINT16 tcpPort_;
UINT16 udpPort_;
//UINT32 ipAddress_;
}; inline UINT32 EndPointToUInt32(struct EndPoint* ep){
return *(const UINT32*)(ep); //buug here
} struct EndPoint endpoint = {0x8080, 0x1080}; int main()
{
//下句在inline+乱序优化时出错
endpoint.udpPort_ = ; UINT32 tmp2 = EndPointToUInt32(&endpoint);
//UINT32 tmp2 = *(const UINT32*)(&endpoint); //用这一句替换上一句同样出错
srand(tmp2); // for break the optimize
printf("%08x %08x should be same as 00008080\n", tmp2, EndPointToUInt32(&endpoint));
}
运行结果如下:
gcc buggy.c
./a.out
00008080 00008080 should be same as 00008080
gcc -O2 buggy.c
./a.out
10808080 00008080 should be same as 00008080
可以看到打开优化之后EndPointToUInt32这个函数的第一次执行就不正常了。
分析
---------
粗略的分析一下目标码
| gcc直接编译的结果 | 替换掉函数调用后的结果 | gcc -o2编译的结果 |
| movw $0,endpoint+2 | movw $0,endpoint+2 | movl endpoint,%ebx |
| pushl $endpoint | movl endpoint,%eax | subl $28,%esp |
| call EndPiontToUInt32 | movl %eax,-4(%ebp) | pushl %ebx |
| addl $4,%esp | subl $12,%esp | movw $0,endpoint+2 |
| movl %esx,-4(%ebp) | pushl -4(%ebp) | call srand |
| subl $12,%esp | call srand | addl $12,%esp |
| pushl -4(%ebp) | addl $16,%esp | pushl endpoint |
| call srand | subl $4,%esp | pushl %ebx |
| addl %16,%esp | pushl $endpoint | pushl $.LC0 |
| subl $4,%esp | call EndPointToUInt32 | call printf |
| pushl $endpoint | addl $4,%esp | |
| call EndPointToUInt32 | pushl %eax | |
| addl $4,%esp | pushl -4(%ebp) | |
| pushl %eax | pushl $.LC0 | |
| pushl -4(%ebp) | call printf | |
| pushl $.LC0 | ||
| call printf | ||
左边的是优化之前的代码,然后movw置endpoint的一半为0,然后取出endpoint的地址调用EndPointToUInt32,并把结果放到tmp2也就是-4(%ebp)中。
中间的代码是将函数inline化以后的结果,注意到现在直接把endpoint的内容通过%eax传给了tmp2也就是-4(%ebp)
右边的代码经过了-o2优化,首先做了一次inline操作,取消了对EndPointToUInt32的调用,也就是直接把endpoint的内容作为EndPointToUInt32的返回值来处理。其次,取消了tmp2变量,用%ebx来替代。至此都没有问题。
问题在于将movw $0,endpoint+2一句优化到了movl endpoint, %ebx的后面。这里做了一个错误的乱序优化。这是因为首先gcc没有能够正确的判断出*(const UINT32*)(&endpoint)实际上和endpoint.udpPort_是相关的,从而优化出错。本来这也是可以容忍的,毕竟写法太变态。但是gcc又在处理inline时过于冒进,没有按照真正的函数调用那样在函数调用处设置一个边界,阻止函数调用前后的代码混杂,而是像一个宏展开一样简单的处理了,最后导致了和预想不一致的结果。
结论
---------
gcc除少数版本外,在-o2乱序优化时都不够完善,不能正确判断代码的影响范围,从而做出错误的乱序。所以请不要引入一些编译器难以判断影响范围的语句,尤其是胡乱cast。典型的如上面程序中的*(const UINT32*)(ep);
gcc的乱序优化对inline函数是像宏展开一样处理的,这可能导致将函数和函数附近的代码乱序,需要小心,常用的FC3/FC5上的gcc都有此问题。
乱序优化与GCC的bug的更多相关文章
- volatile关键字及编译器指令乱序总结
本文简单介绍volatile关键字的使用,进而引出编译期间内存乱序的问题,并介绍了有效防止编译器内存乱序所带来的问题的解决方法,文中简单提了下CPU指令乱序的现象,但并没有深入讨论. 以下是我搭建的博 ...
- 【操作系统之十一】任务队列、CPU Load、指令乱序、指令屏障
一.CPU Loadcpu load是对使用或者等待cpu进程的统计(数量的累加):每一个使用(running)或者等待(runnable)CPU的进程,都会使load值+1;每一个结束的进程,都会使 ...
- sort排序bug乱序
项目需要对组件的zIndex值进行降序排列,刚开始采用的是sort进行排序,排完之后感觉没问题,毕竟也是经常用的,可是昨天无意中把zIndex值打出来看,一看不知道,发现只要排序的组件超过10个就出问 ...
- Chrome谷歌浏览器中js代码Array.sort排序的bug乱序解决办法
[现象] 代码如下: var list = [{ n: "a", v: 1 }, { n: "b", v: 1 }, { n: "c", v ...
- memory barrier 内存屏障 编译器导致的乱序
小结: 1. 很多时候,编译器和 CPU 引起内存乱序访问不会带来什么问题,但一些特殊情况下,程序逻辑的正确性依赖于内存访问顺序,这时候内存乱序访问会带来逻辑上的错误, 2. https://gith ...
- 由乱序播放说开了去-数组的打乱算法Fisher–Yates Shuffle
之前用HTML5的Audio API写了个音乐频谱效果,再之后又加了个播放列表就成了个简单的播放器,其中弄了个功能是'Shuffle'也就是一般播放器都有的列表打乱功能,或者理解为随机播放. 但我觉得 ...
- 关于乱序(shuffle)与随机采样(sample)的一点探究
最近一个月的时间,基本上都在加班加点的写业务,在写代码的时候,也遇到了一个有趣的问题,值得记录一下. 简单来说,需求是从一个字典(python dict)中随机选出K个满足条件的key.代码如下(py ...
- fastjson存在乱序的问题
现象及原因 通常来讲,在使用json数据格式时一般不需要要求数据有序.但凡事都有例外,针对查询时序数据这样一个场景,就必须要求服务器端返回的数据是按时间有序的,否则前端在进行数据展示时就会有问题. 项 ...
- 【转】C 编译器优化过程中的 Bug
C 编译器优化过程中的 Bug 一个朋友向我指出一个最近他们发现的 GCC 编译器优化过程(加上 -O3 选项)里的 bug,导致他们的产品出现非常诡异的行为.这使我想起以前见过的一个 GCC bug ...
随机推荐
- GWT实现“跟随标题栏”
在一些商城或者博客上,随处可见一些“跟随的标题栏”,什么是”跟随的标题栏“呢?它的效果是当浏览器窗口移到看不见某处内容的时候,这块内容的标题会紧贴在浏览器顶部,跟随浏览器滑动,当用户将浏览器内容往上拖 ...
- Annotation之四:注解中的-Xlint:unchecked和 -Xlint:deprecation
一.-Xlint:unchecked用法 对如下Test.java编译时 package com.dxz.annotation; import java.util.ArrayList; import ...
- MySQL mysqldump 备份脚本(按照db.sql)
mysqldump逻辑备份,按照db.sql文件区分,并压缩 #! /bin/bash #35 02 * * * mysql /data/mysqldata/scripts/mysqldump_per ...
- Git学习笔记(三)远程库(GitHub)协同开发,fork和忽略特殊文件
远程库 远程库,通俗的讲就是不再本地的git仓库!他的工作方式和我们本地的一样,但是要使用他就需要先建立连接! 远程库有两种,一个是自己搭建的git服务器:另一种就是使用GitHub,这个网站就是提供 ...
- UIBezierPath和CAShapeLayer配合肆意画图
一.CAShapeLayer CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形 使用CAShapeLayer 绘制一个矩形 let layer ...
- mysql使用存储过程插入数据后,参数为中文的为?或乱码
最近了解了一下mysql存储过程,之前版本的mysql不支持存储过程,5.0版本后就可以支持存储过程的使用:恰好笔者下载使用版本为5.6.20: 做了一个给表插入数据的简单存储过程,发现打开表后汉字全 ...
- 用于确保页面中js加载完全,对于优化某网页的加载速度,有什么见解
js方法: <script type="text/javascript"> window.onload = function(){ var userName = &qu ...
- Android 自定义Camera 随笔
一.权限 <uses-permission android:name="android.permission.CAMERA" /> <uses-permiss ...
- 1-1+zookeeper简介
zookeeper是中间件,可以为分布式系统提供协调服务.如果没有分布式系统,zookeeper也发挥不了它的优势.
- JavaScript基础笔记集合(转)
JavaScript基础笔记集合 JavaScript基础笔记集合 js简介 js是脚本语言.浏览器是逐行的读取代码,而传统编程会在执行前进行编译 js存放的位置 html脚本必须放在&l ...