-O2优化会破坏某些机器上的C ++代码

时间:2012-11-19 17:29:50

标签: linux g++

我有3台不同的机器,配置如下:

  1. OpenSuSe 12.1,linux kernel 3.1.10,gcc 4.6.2
  2. Debian 6,linux kernel 2.6.32,gcc 4.4.5
  3. CentOS 5.6,linux内核2.6.18,gcc 4.1.2
  4. 他们所有的架构都是x86_64。请注意,CentOS'软件版本比Debian更旧,但OpenSuSe软件版本比Debian版本更新。

    我有以下示例代码:

    #include <cstdio>
    #include <cstdlib>
    
    unsigned int cols=5;
    unsigned int rows=6;
    
    int main()
    {
            //allocating...
            double **mat=new double*[rows];
            double *col=new double[rows];
            for(unsigned int i=0;i<rows;++i)
            {
                    mat[i]=new double[cols];
            }
    
            //filling with something...
            for(unsigned int i=0;i<rows;++i)
            {
                    for(unsigned int j=0;j<cols;++j)
                    {
                            mat[i][j]=i+j;
                    }
            }
    
            //testing...
            unsigned long long sum,add;
            for(unsigned int i=0;i<cols;++i)
            {
                    sum=0;
                    for(unsigned int j=0;j<rows;++j)
                    {
                            col[j]=mat[j][i];
                            add=*((unsigned long long*) (&(col[j])));
                            sum+=add;
                    }
    
                    printf("%llu\n",sum);
            }
    
            return 0;
    }
    

    如果我编译此代码时没有任何选项:

    g++ code.cpp
    

    它在所有机器上以相同的方式运行。

    但是如果我用-O2编译它,它在第一台和第三台机器上的行为方式相同,但在第二台机器(Debian)上,-O2优化会破坏它。

    这是带有-O2的第一台机器上此代码的输出:

    4619567317775286272
    9238008735643729920
    9250393634618998784
    9259400833873739776
    9266719183268216832
    

    这是在-O2的第二台机器上:

    0
    4619567317775286272
    9238008735643729920
    9250393634618998784
    9259400833873739776
    

    第二台机器的输出看起来像第一台机器。输出,向下移动一行。

    如果我将(&(col[j]))替换为(&(mat[j][i])),则代码开始正常工作。

    以下是具有-v-O2选项的第二台计算机上的编译器输出:

    Using built-in specs.
    Target: x86_64-linux-gnu
    Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
    Thread model: posix
    gcc version 4.4.5 (Debian 4.4.5-8) 
    COLLECT_GCC_OPTIONS='-v' '-O2' '-shared-libgcc' '-mtune=generic'
     /usr/lib/gcc/x86_64-linux-gnu/4.4.5/cc1plus -quiet -v -D_GNU_SOURCE dmtest.cpp -quiet -dumpbase dmtest.cpp -mtune=generic -auxbase dmtest -O2 -version -o /tmp/cc6v7CNY.s
    ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
    ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../x86_64-linux-gnu/include"
    ignoring nonexistent directory "/usr/include/x86_64-linux-gnu"
    #include "..." search starts here:
    #include <...> search starts here:
     /usr/include/c++/4.4
     /usr/include/c++/4.4/x86_64-linux-gnu
     /usr/include/c++/4.4/backward
     /usr/local/include
     /usr/lib/gcc/x86_64-linux-gnu/4.4.5/include
     /usr/lib/gcc/x86_64-linux-gnu/4.4.5/include-fixed
     /usr/include
    End of search list.
    GNU C++ (Debian 4.4.5-8) version 4.4.5 (x86_64-linux-gnu)
            compiled by GNU C version 4.4.5, GMP version 4.3.2, MPFR version 3.0.0-p3.
    GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
    Compiler executable checksum: 5a2e15051eaa06a84cf6320b754ba993
    COLLECT_GCC_OPTIONS='-v' '-O2' '-shared-libgcc' '-mtune=generic'
     as -V -Qy -o /tmp/ccL37GHG.o /tmp/cc6v7CNY.s
    GNU assembler version 2.20.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.20.1-system.20100303
    COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/
    LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../:/lib/:/usr/lib/
    COLLECT_GCC_OPTIONS='-v' '-O2' '-shared-libgcc' '-mtune=generic'
     /usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../.. /tmp/ccL37GHG.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crtn.o
    

    这种行为的原因是什么?我的代码是不正确的还是gcc 4.4.5中的错误?

2 个答案:

答案 0 :(得分:4)

这是类型惩罚,因此是未定义的行为。我很惊讶你没有得到关于它的警告。

当然,它通常以您期望的方式工作,但它仍然是未定义的行为。

要绕过未定义的行为部分,可以转换为char *并连续读取每个字节。这会解决您所看到的行为吗?

答案 1 :(得分:2)

根据this blog entry,GCC 4.4对-O2强制执行严格的别名规则。因此,类型惩罚访问会导致未定义的行为。不幸的是,为了获得违反严格别名规则的警告,您必须使用-Wstrict-aliasing进行编译。

可能较新版本的GCC已恢复旧行为,并决定是否对用户启用严格别名。