2010-04-09 5 views
8

J'essaie de comprendre à quoi ressemble une boucle while dans IL. J'ai écrit cette fonction C#:While boucle dans IL - pourquoi stloc.0 et ldloc.0?

static void Brackets() 
    { 
     while (memory[pointer] > 0) 
     { 
      // Snipped body of the while loop, as it's not important 
     } 
    } 

L'IL ressemble à ceci:

.method private hidebysig static void Brackets() cil managed 
{ 
    // Code size  37 (0x25) 
    .maxstack 2 
    .locals init ([0] bool CS$4$0000) 
    IL_0000: nop 
    IL_0001: br.s  IL_0012 
    IL_0003: nop 
    // Snipped body of the while loop, as it's not important 
    IL_0011: nop 
    IL_0012: ldsfld  uint8[] BFHelloWorldCSharp.Program::memory 
    IL_0017: ldsfld  int16 BFHelloWorldCSharp.Program::pointer 
    IL_001c: ldelem.u1 
    IL_001d: ldc.i4.0 
    IL_001e: cgt 
    IL_0020: stloc.0 
    IL_0021: ldloc.0 
    IL_0022: brtrue.s IL_0003 
    IL_0024: ret 
} // end of method Program::Brackets 

Pour la plupart ce qui est vraiment simple, à l'exception de la partie après tbc.

Ce que je ne comprends pas est le local [0] et le stloc.0/ldloc.0. Dans la mesure où je le vois, cgt pousse le résultat à la pile, stloc.0 obtient le résultat de la pile dans la variable locale, ldloc.0 repousse le résultat dans la pile et brtrue.s lit la pile.

Quel est le but de cette opération? Cela ne pourrait-il pas être raccourci à juste cgt suivi de brtrue.s?

+1

Un nugget supplémentaire pour vous, basé sur "comme c'est pas important" et votre flux twitter ... notez la différence entre 'br * _s' et' br * '. Les variantes '_s' utilisent un petit décalage * relatif *, et ** ne fonctionnera pas ** si les emplacements ne sont pas proches les uns des autres (les variantes non-'_s' utilisent un décalage * absolu * sans la" petite "limitation). Donc, si votre compilateur ne peut pas prédire (à l'avance) la taille du corps, utilisez 'br *' de préférence 'br * _s'. –

+0

@Marc Merci, a rencontré exactement ce problème: D Mon application Démo a une seule instruction pour que le compilateur génère des variantes _s, mais mon vrai compilateur avait un corps de la boucle beaucoup plus long. Effet secondaire positif: J'ai appris quelque chose à partir de cette "Exception illégale d'une branche d'octets" :) –

+0

IIRC, ILGenerator est assez intelligent pour émettre l'opcode correct pour la situation actuelle si vous utilisez la bonne méthode. –

Répondre

6

Essayez de compiler avec des optimisations.

+0

Cela "corrige" en fait - est-ce seulement là pour Debug/Breakpoint? Juste curieux de savoir pourquoi il est généré en premier lieu. –

+0

Je suppose que les deux instructions qui s'annulent proviennent de différentes parties du compilateur. Je pourrais définitivement imaginer que toutes les boucles while sont initialement converties en nop (en tant que cible de saut) .... ldloc. brtrue.s (addr de nop). Cela semble assez sain si vous savez que l'optimiseur enlèvera le nop inutile et ainsi de suite au passage suivant. –

4

Ceci est une version de débogage (à partir du nop). Tous les paris sont ouverts, mais il semble que il est introduit simplement une variable bool pour la simplicité:

goto testforexit; 
body: 
    .. 
testforexit: 
    bool tmp = memory[pointer] > 0; 
    if(tmp) goto body; 

Construire dans la version avec Optimisations activée, ce qui devrait éliminer ces variables.

Questions connexes