Saat menulis ftol
fungsi yang dioptimalkan saya menemukan beberapa perilaku yang sangat aneh di GCC 4.6.1
. Biarkan saya tunjukkan kode terlebih dahulu (untuk kejelasan saya menandai perbedaannya):
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 */
}
Tampak sama kan? GCC tidak setuju. Setelah dikompilasi dengan gcc -O3 -S -Wall -o test.s test.c
ini adalah output perakitan:
fast_trunc_one, dihasilkan:
_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, dihasilkan:
_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
Itu perbedaan ekstrem . Ini sebenarnya muncul di profil juga, fast_trunc_one
sekitar 30% lebih cepat daripada fast_trunc_two
. Sekarang pertanyaan saya: apa yang menyebabkan ini?
-S -O3 -da -fdump-tree-all
. Ini akan membuat banyak snapshot dari representasi perantara. Berjalan melalui mereka (diberi nomor) berdampingan dan Anda harus dapat menemukan optimasi yang hilang dalam kasus pertama.
int
menjadi unsigned int
dan lihat apakah perbedaannya hilang.
(r + shifted) ^ sign
tidak sama dengan r + (shifted ^ sign)
. Saya kira itu membingungkan pengoptimal? FWIW, MSVC 2010 (16.00.40219.01) menghasilkan daftar yang hampir identik satu sama lain: gist.github.com/2430454