34 votes

Pourquoi les instructions AND sont-elles générées?

Pour le code comme ceci:

int res = 0;
for (int i = 0; i < 32; i++)
{
    res += 1 << i;
}

Ce code est généré (en mode release, pas de débogueur, 64 bits):

 xor edx,edx 
 mov r8d,1 
_loop:
 lea ecx,[r8-1] 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 mov ecx,r8d 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 lea ecx,[r8+1] 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 lea ecx,[r8+2] 
 and ecx,1Fh     ; why?
 mov eax,1 
 shl eax,cl 
 add edx,eax 
 add r8d,4 
 cmp r8d,21h 
 jl  _loop

Maintenant, je peux voir le point de la plupart des instructions, mais qu'est-ce avec la ET des instructions? ecx va jamais être plus de 0x1F dans le présent code, de toute façon, mais j'ai une excuse pour ne pas remarquer que (et aussi pour ne pas avoir remarqué que le résultat est une constante), ce n'est pas une avance sur le temps compilateur qui peut se permettre de passer beaucoup de temps sur l'analyse après tout. Mais plus important encore, SHL avec un 32 bits opérande masques cl par 0x1F déjà. Il me semble donc que ces ANDs sont tout à fait inutile. Pourquoi sont-ils générés? Ont-ils une raison qui m'échappe?

27voto

romkyns Points 17295

L' and est déjà présent dans le code CIL émis par le compilateur C#:

    IL_0009: ldc.i4.s 31
    IL_000b: and
    IL_000c: shl

Les spécifications du CIL shl instruction dit:

La valeur de retour n'est pas spécifié si shiftAmount est supérieure ou égale à la taille de la valeur.

Le C# spec, cependant, définit les 32 bits de décalage de prendre le décalage mod 32:

Lorsque le type de x est int ou uint, le décalage est donné par la faible-de l'ordre de cinq bits de comte. En d'autres termes, le décalage est calculé à partir de count & 0x1F.

Dans ce cas, le compilateur C# ne peut pas vraiment faire beaucoup mieux que n'émettent explicite and de l'opération. Le mieux que vous pouvez espérer, c'est que la Gigue remarquerez que cette et optimiser loin redondant and, mais cela prend du temps, et la vitesse de JIT est assez important. Alors, pensez-y le prix payé pour une équipe commune d'enquête à base de système.

La vraie question, je pense, c'est pourquoi le CIL spécifie l' shl instruction de cette façon, quand C# et x86, les deux préciser les tronquer comportement. Que je ne sais pas, mais je pense que c'est important pour le CIL spec pour éviter de spécifier un comportement qui peut JIT pour quelque chose de cher sur certains jeux d'instructions. Dans le même temps, il est important pour le C# pour avoir que quelques pas défini les comportements que possible, parce que les gens, au final, l'utilisation de tels comportements indéfinis jusqu'à la prochaine version du compilateur/cadre/OS/quelles que soient les variations de mer, de casser le code.

10voto

Hans Passant Points 475940

x64 cœurs appliquent déjà les 5 bits de masque pour le décalage. À partir du Processeur Intel manuel, tome 2B page 4-362:

L'opérande de destination peut être un registre ou d'un emplacement mémoire. Le comte opérande peut être une valeur immédiate ou de la CL de registre. Le comte est masqué à 5 bits (ou 6 bits si en mode 64 bits et REG.W est utilisé). Un spécial opcode encodage est prévu pour un nombre de 1.

Donc, c'est du code machine qui n'est pas nécessaire. Malheureusement, le compilateur C# ne peut pas faire des hypothèses sur le processeur de comportement et doit s'appliquer C# les règles de la langue. Et de générer des IL dont le comportement est spécifié dans la spécification CLI. Ecma-335, Partion III, chapitre 3.58 dit ceci à propos de la SHL opcode:

La shl instruction changements de valeur (int32, int64 ou native int) laissé par le nombre de bits spécifié par shiftAmount. shiftAmount est de type int32 ou native int. La valeur de retour n'est pas spécifié si shiftAmount est supérieure ou égale à la largeur de valeur.

Indéterminée est le problème ici. La montaison comportement spécifié sur le dessus d'une quelconque mise en œuvre de détails produit le code inutile. Techniquement, la gigue pourrait optimiser l'opcode loin. Même si c'est délicat, il ne connaît pas le langage de la règle. Une langue qui ne spécifie pas de masquage aurez un moment difficile de générer bon IL. Vous pouvez poster à connect.microsoft.com pour obtenir la gigue de l'équipe de vue sur la question.

5voto

Evgeny Kluev Points 16685

Le compilateur C # doit insérer ces instructions AND lors de la génération du code intermédiaire (indépendant de la machine), car l'opérateur de décalage à gauche C # doit utiliser uniquement les 5 bits les moins significatifs.

Lors de la génération de code x86, l’optimisation du compilateur peut supprimer ces instructions inutiles. Mais, apparemment, cette optimisation est ignorée (probablement parce qu’elle ne peut pas consacrer beaucoup de temps à l’analyse).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X