LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: platinum

Gentoo 目前 stable 的 GCC 可能存在 BUG!

[复制链接]
发表于 2009-12-3 14:19:32 | 显示全部楼层
Post by Etrnls;2051983
简单看了一下,开了O2以后传给printf的参数前三个都是直接传的立即数0,也就是gcc认为test这个变量没被修改,所以直接常量传播到了printf的参数,(但是为什么第四个是正确的-_-|||,我再研究研究……)所以上面那句话可以从循环里面拿出来放在printf前面,结果就正确了


首先很抱歉,上面说的把那句asm放在printf前面结果就正确了是我想当然了,并没有经过试验,事实证明是不正确的。
这个问题大概是这个样子的,加了O2以后gcc把data_set_hard放到了for循环后面printf前面,似乎gcc认为这个for循环不会对test造成改变,所以printf传入的参数是立即数0而不是从内存取出的实际值……还是上代码看吧,感觉这样解释不清楚

  1. main:
  2. ......
  3. movl $0, -32(%ebp) ; 这里是对于test的初始化,如果test定义的时候没有={0}那么就不存在这几句
  4. movl $0, -28(%ebp)
  5. movl $0, -24(%ebp)
  6. movl $0, -20(%ebp)
  7. .L2:
  8. ...... ;这里若干行是展开的data_test
  9. cmp $128, %edx
  10. jne .L2 ; 这就是那个for循环,这里面的代码是没有问题的

  11. movl -20(%ebp), %eax
  12. movl $0, -24(%ebp) ;注意这里的赋0其实是data_set_hard的代码,删掉data_set_hard是不存在这里的赋0的
  13. movl $0, -28(%ebp)
  14. movl $0, -32(%ebp)
  15. movl %eax, 20(%esp) ; 这是唯一一个正确的数,不明白为什么会有一个正确的
  16. movl $0, 16(%esp) ; 这里传给printf的参数都是0,事实上就是从data_set_hard常量传播过来的,因为这里其实就类似于 a = 0; printf("%d", a)一样
  17. movl $0, 12(%esp)
  18. movl $0, 8(%esp)
  19. movl $.LC0, 4(%esp)
  20. movl $1 (%esp)
  21. call __printf_chk
  22. ......
复制代码

可以看到data_set_hard被优化到了循环后面,但是似乎又不完整,只对三个成员进行了赋0,而紧接着的printf的参数自然可以根据前面的赋值得到,从而减少一次内存访问,同时由于前面很奇怪的只有三个赋0,所以printf的第四个数是从内存里面取出来的,所以是正确的。
经试验,删掉data_set_hard会导致printf的参数全部从内存中读取,所以结果是正确的。
我认为gcc之所以敢把data_set_hard放在循环后面,是因为它认为这个循环并没有修改test,而作出这个判断正是因为使用了指针指向了其他变量并通过这个指针修改它的值,至于是不是bug,我记得标准只是规定了编译器可以认为不同类型的指针必然指向不同的东西,然而这里的确是相同的指针(__u32*),所以可能的确是个bug,老大来看看?
回复 支持 反对

使用道具 举报

发表于 2009-12-3 14:28:22 | 显示全部楼层
我的gcc测试之后有楼主所说的现象

gcc信息如下

Using built-in specs.
Target: x86_64-pc-linux-gnu
Configured with: /var/tmp/portage/sys-devel/gcc-4.3.3-r2/work/gcc-4.3.3/configure --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/4.3.3 --includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.3.3/include --datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.3.3 --mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.3.3/man --infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.3.3/info --with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.3.3/include/g++-v4 --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --disable-altivec --disable-fixed-point --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --enable-secureplt --enable-multilib --enable-libmudflap --disable-libssp --enable-libgomp --enable-cld --disable-libgcj --enable-languages=c,c++,treelang,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --with-bugurl=http://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.3.3-r2 p1.2, pie-10.1.5'
Thread model: posix
gcc version 4.3.3 (Gentoo 4.3.3-r2 p1.2, pie-10.1.5)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-3 14:36:39 | 显示全部楼层
Post by Etrnls;2052006
首先很抱歉,上面说的把那句asm放在printf前面结果就正确了是我想当然了,并没有经过试验,事实证明是不正确的。


我的测试发现,用 __asm__ __volatile__ ("":::"memory"); 替换 printf,结果是正确的
完整代码如下

  1. #include <stdio.h>
  2. #include <linux/types.h>

  3. #define UNIT_BITS       32

  4. struct data {
  5.         __u32 d3;
  6.         __u32 d2;
  7.         __u32 d1;
  8.         __u32 d0;
  9. };

  10. static inline void
  11. data_set_hard (struct data *ret,
  12.                __u32 a, __u32 b, __u32 c, __u32 d)
  13. {
  14.         ret->d3 = a;
  15.         ret->d2 = b;
  16.         ret->d1 = c;
  17.         ret->d0 = d;
  18. }

  19. static inline void
  20. data_set (struct data *ret, int bit)
  21. {
  22.         __u32 *data[4];
  23.         int a, b;

  24.         a = bit / UNIT_BITS;
  25.         b = bit % UNIT_BITS;

  26.         data[0] = &ret->d0;
  27.         data[1] = &ret->d1;
  28.         data[2] = &ret->d2;
  29.         data[3] = &ret->d3;

  30.         *data[a] |= 1 << b;
  31.         __asm__ __volatile__ ("":::"memory");
  32. }

  33. int
  34. main (void)
  35. {
  36.         struct data test = {0};
  37.         int i;

  38.         data_set_hard(&test, 0, 0, 0, 0);

  39.         for (i=0; i<UNIT_BITS*4; i++) {
  40.                 data_set(&test, i);
  41.         }

  42.         printf ("test = %.8x, %.8x, %.8x, %.8x\n",
  43.                 test.d3, test.d2, test.d1, test.d0);

  44.         return 0;
  45. }
复制代码

# gcc -O2 -Wall -o test test.c
# ./test
test = ffffffff, ffffffff, ffffffff, ffffffff

# gcc -v
使用内建 specs。
目标:i686-pc-linux-gnu
配置为:/var/tmp/portage/sys-devel/gcc-4.3.4/work/gcc-4.3.4/configure --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/4.3.4 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/4.3.4 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/4.3.4/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/4.3.4/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4 --host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --disable-fixed-point --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --enable-secureplt --disable-multilib --enable-libmudflap --disable-libssp --enable-libgomp --disable-libgcj --with-arch=i686 --enable-languages=c,c++,treelang,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --with-bugurl=http://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.3.4 p1.0, pie-10.1.5'
线程模型:posix
gcc 版本 4.3.4 (Gentoo 4.3.4 p1.0, pie-10.1.5)
回复 支持 反对

使用道具 举报

发表于 2009-12-3 14:50:43 | 显示全部楼层
Post by platinum;2052015
我的测试发现,用 __asm__ __volatile__ ("":::"memory"); 替换 printf,结果是正确的
完整代码如下

  1. #include <stdio.h>
  2. #include <linux/types.h>

  3. #define UNIT_BITS       32

  4. struct data {
  5.         __u32 d3;
  6.         __u32 d2;
  7.         __u32 d1;
  8.         __u32 d0;
  9. };

  10. static inline void
  11. data_set_hard (struct data *ret,
  12.                __u32 a, __u32 b, __u32 c, __u32 d)
  13. {
  14.         ret->d3 = a;
  15.         ret->d2 = b;
  16.         ret->d1 = c;
  17.         ret->d0 = d;
  18. }

  19. static inline void
  20. data_set (struct data *ret, int bit)
  21. {
  22.         __u32 *data[4];
  23.         int a, b;

  24.         a = bit / UNIT_BITS;
  25.         b = bit % UNIT_BITS;

  26.         data[0] = &ret->d0;
  27.         data[1] = &ret->d1;
  28.         data[2] = &ret->d2;
  29.         data[3] = &ret->d3;

  30.         *data[a] |= 1 << b;
  31.         __asm__ __volatile__ ("":::"memory");
  32. }

  33. int
  34. main (void)
  35. {
  36.         struct data test = {0};
  37.         int i;

  38.         data_set_hard(&test, 0, 0, 0, 0);

  39.         for (i=0; i<UNIT_BITS*4; i++) {
  40.                 data_set(&test, i);
  41.         }

  42.         printf ("test = %.8x, %.8x, %.8x, %.8x\n",
  43.                 test.d3, test.d2, test.d1, test.d0);

  44.         return 0;
  45. }
复制代码

# gcc -O2 -Wall -o test test.c
# ./test
test = ffffffff, ffffffff, ffffffff, ffffffff

# gcc -v
使用内建 specs。
目标:i686-pc-linux-gnu
配置为:/var/tmp/portage/sys-devel/gcc-4.3.4/work/gcc-4.3.4/configure --prefix=/usr --bindir=/usr/i686-pc-linux-gnu/gcc-bin/4.3.4 --includedir=/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include --datadir=/usr/share/gcc-data/i686-pc-linux-gnu/4.3.4 --mandir=/usr/share/gcc-data/i686-pc-linux-gnu/4.3.4/man --infodir=/usr/share/gcc-data/i686-pc-linux-gnu/4.3.4/info --with-gxx-include-dir=/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4 --host=i686-pc-linux-gnu --build=i686-pc-linux-gnu --disable-altivec --disable-fixed-point --enable-nls --without-included-gettext --with-system-zlib --disable-checking --disable-werror --enable-secureplt --disable-multilib --enable-libmudflap --disable-libssp --enable-libgomp --disable-libgcj --with-arch=i686 --enable-languages=c,c++,treelang,fortran --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --with-bugurl=http://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.3.4 p1.0, pie-10.1.5'
线程模型:posix
gcc 版本 4.3.4 (Gentoo 4.3.4 p1.0, pie-10.1.5)

囧……偶又米有说清楚……
我前面说的都是基于最开始的代码的,我第一次说加一行asm,我是加在了
for (...)
{
data_set...
__asm__ ....
}
后来我说其实可以放在printf前面,我的意思其实是
for (...)
{
data_set...
}

__asm__...
printf(...)

我的本意是想让最后这个输出结果的printf强制从内存取值(因为我发现前面的循环的代码是没问题的,只是最后输出的结果是不对的),但是我发现不行,因为像上文说的,似乎data_set_hard被放在后面了,所以还是结果不对
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-3 15:06:06 | 显示全部楼层
呵呵,对不起啊是我没理解
在 forums.gentoo.org 上我也问了,老外们也承认这可能的确是个 BUG,建议我加 -fno-inline 参数强行不允许 inline 试试,结果正常

他们说可以报告给官方
回复 支持 反对

使用道具 举报

发表于 2009-12-3 15:26:56 | 显示全部楼层
删掉特意装的4.3.4了~还是4.4好~吼吼

老大有空看一眼?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-3 15:34:44 | 显示全部楼层
但是 4.4 也有问题,我在用 genkernel 编译 2.6.23 内核时有错误
进入内核源码目录,手动 make 也无法编译,提示很奇怪,提示找不到 dil 寄存器

查了一下资料,发现 4.4 对 CPU 构架选择后调用汇编处理时有问题

详情请见:
http://fr.pastebin.ca/1698152
http://portabilityblog.com/blog/ ... ame-dil-or-sil.html

我现在换回 4.1.2 了,一切都正常,有的时候,贪小便宜吃大亏,时髦有的时候玩不得……
回复 支持 反对

使用道具 举报

发表于 2009-12-3 15:35:01 | 显示全部楼层
疯了。为了解决[color="Blue"]amule无法启动的问题,从4.4.2换了4.4.1,又把4.3.4,4.3.2-r3挨个试了,每次都重做整个系统,都不行。最后用了4.3.2-r3,现在又出现这样的问题。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-3 16:52:13 | 显示全部楼层
目前 4.1.2 还是相对正确的,没有 -O2 时的 inline 问题,也没有编译内核时产生的没有 dil 寄存器问题
回复 支持 反对

使用道具 举报

发表于 2009-12-3 19:19:22 | 显示全部楼层
Post by coderoar;2052035
疯了。为了解决[color="Blue"]amule无法启动的问题,从4.4.2换了4.4.1,又把4.3.4,4.3.2-r3挨个试了,每次都重做整个系统,都不行。最后用了4.3.2-r3,现在又出现这样的问题。


别提了,曾经为amule头都大了,不过,还是先不用amule,等哪天修正好了,再换
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表