关于gcc412的优化bug

之前在搜狗曾经看过一篇技术文章,详细讲述了gcc编译器的一个优化bug,也就是在开-O2的时候,汇编指令不对。但是没往心里去,逐渐淡忘。

前一段时间申请了虚机,就在上面yum install gcc,默认提供的还是4.1.2版本的。这也造成了我的一个悲剧(因为这个bug折腾了半天)。

一般没有人会去怀疑编译器的,是吧?


进入正题,最近在写一个小的网络程序,用C++写server,用java写client,因为网络传输涉及到byte order的问题,一般是统一转成big endian。

我发现有htonl,htons(当然他们的能力是32bit和16bit,我需要的是64bit,所以自己实现了一个htond),但是我要的数据是double,不好做位操作,怎么办呢?一个极其简单的方法就是将其转为long,64位机器long和double都是8字节。

double  v;
uint64_t  x = *(uint64_t*)&v;
x = htond(x);
v = *(double*)&x;

我先用C++实现了一个客户端(本人对java不熟),但是接收到的数据和我传输的不一致。经过一步步打印,发现数据确实正确的传到了客户端,但是就是经过一个double-long-double的过程,数据打印出来就不对。我甚至怀疑,我的这个转换方法难道有错?

后来写了一个小实验程序,验证问题,发现开O2的时候,结果出错,如果是O0,结果正确。

int main() {
        uint64_t  z = 0x1234567890abcdef;
        double x;
        x = *(double*)&z;
        uint64_t  y = *(uint64_t*)&x;
        printf( "%lx\n", y );
        return 0;
}
这段程序,在O2下输出0,在O0下输出1234567890abcdef。

对比了一下这段程序在两种情况下的汇编代码:

O2O0
main:
.LFB14:
        subq    $24, %rsp
.LCFI0:
        movabsq $1311768467294899695, %rax
        movl    $.LC0, %edi
        movq    %rax, 16(%rsp)
        movq    16(%rsp), %rax
        movq    8(%rsp), %rsi
        movq    %rax, 8(%rsp)
        xorl    %eax, %eax
        call    printf
        xorl    %eax, %eax
        addq    $24, %rsp
        ret
main:
.LFB2:
        pushq   %rbp
.LCFI0:
        movq    %rsp, %rbp
.LCFI1:
        subq    $32, %rsp
.LCFI2:
        movl    $-1867788817, -16(%rbp)
        movl    $305419896, -12(%rbp)
        leaq    -16(%rbp), %rax
        movq    (%rax), %rax
        movq    %rax, -24(%rbp)
        leaq    -24(%rbp), %rax
        movq    (%rax), %rax
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rsi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        ret

可以看到,O2的情况下,也还是很傻的从%rax》16(%rsp)》%rax的挪动数据,并且将未被赋值的8(%rsp)的值先放在了%rsi,然后才更新了8(%rsp)的值,

导致%rsi里面是一个随机值。O0情况下,立即数是分次装入的,但是执行次序没有错,所以结果正确。

从这里我们可以猜测,调用printf时,将format字符串放在%edi,将需要打印的数据放在%rsi,将%eax清0,然后call printf就行了。

后来我下载了gcc 4.4.6,然后同样用O2编译,产生了正确的并且更简短的代码,如下:

main:
.LFB12:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movabsq $1311768467294899695, %rsi
        movl    $.LC0, %edi
        xorl    %eax, %eax
        call    printf
        xorl    %eax, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc

所以,如果有还在用老编译器的兄弟,赶紧更新吧。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值