以下是更多数据点:
基本上,它看起来像 gcc 优化(即使在 -O 标志关闭且 -g 开启时):
[variable] < (type-cast)(1 << [variable2])
至
((type-cast)[variable] >> [variable2]) == 0
和
[variable] >= (type-cast)(1 << [variable2])
至
((type-cast)[variable] >> [variable2]) != 0
其中 [variable] 需要是数组访问。
我想这里的优点是它不必将文字 1 加载到寄存器中,这样可以节省 1 个寄存器。
所以这里是数据点:
- 将 1 更改为 > 1 会强制它实现正确的版本。
- 将任何变量更改为文字会强制它实现正确的版本
- 将 [variable] 更改为非数组访问会强制它实现正确的版本
- [variable] > (type-cast)(1 << [variable2]) 实现了正确的版本。
我怀疑这一切都是为了保存一个寄存器。当 [variable] 是数组访问时,它还需要保留一个索引。有人可能认为这很聪明,直到它错了。
使用错误报告中的代码http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56051
#include <stdio.h>
int main(void)
{
int a, s = 8;
unsigned char data[1] = {0};
a = data[0] < (unsigned char) (1 << s);
printf("%d\n", a);
return 0;
}
用 gcc -O2 -S 编译
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $8, %esp
pushl $1 ***** seems it already precomputed the result to be 1
pushl $.LC0
pushl $1
call __printf_chk
xorl %eax, %eax
movl -4(%ebp), %ecx
leave
leal -4(%ecx), %esp
ret
只用 gcc -S 编译
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ebx
pushl %ecx
subl $16, %esp
movl $8, -12(%ebp)
movb $0, -17(%ebp)
movb -17(%ebp), %dl
movl -12(%ebp), %eax
movb %dl, %bl
movb %al, %cl
shrb %cl, %bl ****** (unsigned char)data[0] >> s => %bl
movb %bl, %al %bl => %al
testb %al, %al %al = 0?
sete %dl
movl $0, %eax
movb %dl, %al
movl %eax, -16(%ebp)
movl $.LC0, %eax
subl $8, %esp
pushl -16(%ebp)
pushl %eax
call printf
addl $16, %esp
movl $0, %eax
leal -8(%ebp), %esp
addl $0, %esp
popl %ecx
popl %ebx
popl %ebp
leal -4(%ecx), %esp
ret
我想下一步是挖掘 gcc 的源代码。