【发布时间】:2012-05-02 06:52:25
【问题描述】:
在编写优化的ftol 函数时,我在GCC 4.6.1 中发现了一些非常奇怪的行为。让我先给你看代码(为清楚起见,我标记了不同之处):
fast_trunc_one, C:
int fast_trunc_one(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = mantissa << -exponent; /* diff */
} else {
r = mantissa >> exponent; /* diff */
}
return (r ^ -sign) + sign; /* diff */
}
fast_trunc_two, C:
int fast_trunc_two(int i) {
int mantissa, exponent, sign, r;
mantissa = (i & 0x07fffff) | 0x800000;
exponent = 150 - ((i >> 23) & 0xff);
sign = i & 0x80000000;
if (exponent < 0) {
r = (mantissa << -exponent) ^ -sign; /* diff */
} else {
r = (mantissa >> exponent) ^ -sign; /* diff */
}
return r + sign; /* diff */
}
看起来一样吧?那么GCC不同意。使用gcc -O3 -S -Wall -o test.s test.c 编译后,这是汇编输出:
fast_trunc_one,生成:
_fast_trunc_one:
LFB0:
.cfi_startproc
movl 4(%esp), %eax
movl $150, %ecx
movl %eax, %edx
andl $8388607, %edx
sarl $23, %eax
orl $8388608, %edx
andl $255, %eax
subl %eax, %ecx
movl %edx, %eax
sarl %cl, %eax
testl %ecx, %ecx
js L5
rep
ret
.p2align 4,,7
L5:
negl %ecx
movl %edx, %eax
sall %cl, %eax
ret
.cfi_endproc
fast_trunc_two,生成:
_fast_trunc_two:
LFB1:
.cfi_startproc
pushl %ebx
.cfi_def_cfa_offset 8
.cfi_offset 3, -8
movl 8(%esp), %eax
movl $150, %ecx
movl %eax, %ebx
movl %eax, %edx
sarl $23, %ebx
andl $8388607, %edx
andl $255, %ebx
orl $8388608, %edx
andl $-2147483648, %eax
subl %ebx, %ecx
js L9
sarl %cl, %edx
movl %eax, %ecx
negl %ecx
xorl %ecx, %edx
addl %edx, %eax
popl %ebx
.cfi_remember_state
.cfi_def_cfa_offset 4
.cfi_restore 3
ret
.p2align 4,,7
L9:
.cfi_restore_state
negl %ecx
sall %cl, %edx
movl %eax, %ecx
negl %ecx
xorl %ecx, %edx
addl %edx, %eax
popl %ebx
.cfi_restore 3
.cfi_def_cfa_offset 4
ret
.cfi_endproc
这是一个极端的区别。这实际上也显示在配置文件中,fast_trunc_one 比 fast_trunc_two 快 30% 左右。现在我的问题是:是什么原因造成的?
【问题讨论】:
-
出于测试目的,我创建了一个 gist here,您可以在其中轻松复制/粘贴源代码,看看是否可以在其他系统/版本的 GCC 上重现该错误。
-
将测试用例放在自己的目录中。用
-S -O3 -da -fdump-tree-all编译它们。这将创建中间表示的许多快照。并排浏览它们(它们已编号),您应该能够在第一种情况下找到缺失的优化。 -
建议二:将
int全部改成unsigned int,看看差异是否消失。 -
这两个函数的数学运算似乎略有不同。虽然结果可能相同,但表达式
(r + shifted) ^ sign与r + (shifted ^ sign)不同。我想这让优化器感到困惑? FWIW, MSVC 2010 (16.00.40219.01) 生成的列表几乎完全相同:gist.github.com/2430454 -
@DCoder:该死的!我没有发现。但是,这不是差异的解释。让我用一个新版本来更新这个问题,排除这个问题。
标签: c gcc assembly x86 compiler-optimization