Voici d'autres points de données :
En fait, il semble que gcc optimise (même lorsque le drapeau -O est désactivé et que -g est activé) :
[variable] < (type-cast)(1 << [variable2])
à
((type-cast)[variable] >> [variable2]) == 0
y
[variable] >= (type-cast)(1 << [variable2])
à
((type-cast)[variable] >> [variable2]) != 0
où [variable] doit être un accès à un tableau.
Je suppose que l'avantage ici est qu'il n'est pas nécessaire de charger le 1 littéral dans un registre, ce qui économise un registre.
Voici donc les points de données :
- En remplaçant 1 par un nombre > 1, il est obligé d'implémenter la version correcte.
- changer une des variables en un littéral le force à implémenter la version correcte
- changer [variable] en un accès non tableau le force à implémenter la version correcte
- [variable] > (type-cast)(1 << [variable2]) implémente la version correcte.
Je soupçonne que tout cela vise à sauver un registre. Lorsque [variable] est un accès à un tableau, elle doit également conserver un index. Quelqu'un a probablement pensé que c'était très intelligent, jusqu'à ce que ce soit faux.
En utilisant le code du rapport de bogue 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;
}
compilé avec 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
compiler avec seulement 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
Je suppose que la prochaine étape est de creuser dans le code source de gcc.