2009-05-18 4 views
5

Quelqu'un peut-il expliquer ces quelques lignes de MSIL? Pourquoi déplace-t-il une valeur de la pile d'évaluation vers une variable locale, uniquement pour la reculer immédiatement et la renvoyer?Quelqu'un peut-il expliquer ces quelques lignes de MSIL?

Le code MSIL suivant charge un seul argument (une chaîne), appelle une méthode qui renvoie bool et renvoie cette valeur booléenne. Ce que je ne comprends pas, c'est pourquoi il appelle stloc.0 pour stocker la valeur de retour de la méthode dans une variable locale, puis effectue un transfert de contrôle inconditionnel explicite vers la ligne étiquetée suivante (semble inutile), seulement pour replacer la valeur sur la pile d'évaluation avant de la renvoyer.

.maxstack 1 
.locals init ([0] bool CS$1$0000) 
L_0000: nop 
L_0001: ldarg.0 
L_0002: call bool FuncNameNotImporant::MethodNameNotImporant(string) 
L_0007: stloc.0 
L_0008: br.s L_000a 
L_000a: ldloc.0 
L_000b: ret 

Ma meilleure estimation pourquoi il n'est d'effectuer une sorte de vérification de type pour assurer la valeur sur la pile d'évaluation est en fait une valeur booléenne avant de le retourner. Mais je n'ai aucune idée du saut explicite à la ligne suivante; Je veux dire, ne serait-il pas là de toute façon? Le code source C# pour la méthode est juste une ligne, qui renvoie le résultat de la méthode.

Répondre

7

Si vous ouvrez cette fonction dans le débogueur, avec le code compilé en mode débogage:

bool foo(string arg) 
{ 
    return bar(arg); 
} 

Il y a 3 points de rupture que vous pouvez définir:

  1. A l'accolade d'ouverture de la fonction.
  2. Sur la ligne "retour".
  3. A l'accolade fermante de la fonction.

La définition d'un point d'arrêt sur l'accolade d'ouverture signifie "pause lorsque cette fonction est appelée". C'est pourquoi il existe une instruction no-op au début de la méthode. Lorsque le point d'arrêt est défini sur l'accolade ouvrante, le débogueur le définit réellement sur le no-op.

La définition d'un point de rupture sur l'accolade fermante signifie «pause lorsque cette fonction est désactivée». Pour que cela se produise, la fonction doit avoir une seule instruction de retour dans son IL, où le point de rupture peut être défini. Le compilateur permet que, en utilisant une variable temporaire pour stocker la valeur de retour, et la conversion

return retVal; 

en

$retTmp = retVal; 
goto exit; 

puis injecter le code suivant au bas de la méthode:

exit: 
return $ret; 

En outre, en mode de débogage, les compilateurs sont stupides sur le code qu'ils génèrent.Ils font essentiellement quelque chose comme:

GenerateProlog(); 
foreach (var statement in statements) 
{ 
    Generate(statement); 
} 
GenerateEpilog(); 

Dans votre cas, vous voyez:

return foo(arg); 

en cours de traduction en:

; //this is a no-op 
bool retTemp = false; 
retTemp = foo(arg); 
goto exit; 
exit: 
return retTemp; 

Si le compilateur a fait une "optimisation de la fenêtre coulissante" il pourrait être capable de regarder ce code et réaliser qu'il y avait une certaine redondance. Cependant, les compilateurs ne le font généralement pas en mode débogage. Les optimisations du compilateur peuvent faire des choses comme éliminer les variables et réorganiser les instructions, ce qui rend le débogage difficile. Le but d'une version de débogage étant d'activer le débogage, il ne serait pas bon d'activer les optimisations.

Dans une version de version, le code ne ressemblera pas à cela. C'est parce que le compilateur ne présente pas le code spécial pour permettre points d'arrêt sur les bretelles d'ouverture et de fermeture, ce qui laisse tout ce qui suit à compiler:

return bar(arg); 

qui se termine à la recherche assez simple. Une chose à noter, cependant, est que je ne pense pas que le compilateur C# fasse beaucoup d'optimisations de fenêtres glissantes, même dans les versions commerciales. C'est parce que la plupart de ces optimisations dépendent de l'architecture du processeur sous-jacent et donc sont faites par le compilateur JIT. Faire les optimisations, même celles qui sont indépendantes du processeur, dans le compilateur C# peut empêcher le JIT d'optimiser le code (il cherche des modèles générés par une génération de code non optimisée, et s'il voit une IL fortement optimisée, il peut obtenir confus). Donc, les compilateurs de code généralement gérés ne le font pas. Il fait des "choses chères" (que le JIT ne veut pas faire à l'exécution), comme la détection de code mort, et l'analyse de variables en direct, mais elles n'abordent pas les problèmes résolus par l'optimisation de fenêtre glissante.

4

Compilez-vous en mode de débogage ou de publication? En mode de libération, j'obtiens:

.method private hidebysig static bool Test1(string arg) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.0 
    L_0001: call bool FuncNameNotImportant::MethodNameNotImportant(string) 
    L_0006: ret 
} 

La branche que vous voyez est probablement pour le support du débogueur.

+0

semble raisonnable pour moi –

Questions connexes