Here are some more data points:
basically, it looks like gcc optimizes (even in when the -O flag is off and -g is on):
[variable] < (type-cast)(1 << [variable2])
to
((type-cast)[variable] >> [variable2]) == 0
and
[variable] >= (type-cast)(1 << [variable2])
to
((type-cast)[variable] >> [variable2]) != 0
where [variable] needs to be an array access.
I guess the advantage here is that it doesn't have to load the literal 1 into a register, which saves 1 register.
So here are the data points:
- changing 1 to a number > 1 forces it to implement the correct version.
- changing any of the variables to a literal forces it to implement the correct version
- changing [variable] to a non array access forces it to implement the correct version
- [variable] > (type-cast)(1 << [variable2]) implements the correct version.
I suspect this is all trying to save a register. When [variable] is an array access, it needs to also keep an index. Someone probably thought this is so clever, until it's wrong.
Using code from the bug report 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;
}
compiled with 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
compile with just 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
I guess the next step is to dig through gcc's source code.