Question

Quelqu'un peut-il expliquer ces quelques lignes de MSIL? Pourquoi faut-il déplacer une valeur de la pile d'évaluation à une variable locale, pour le ramener immédiatement et le retourner?

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

.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 renvoyer. Mais je suis la moindre idée de saut explicite à la ligne suivante; Je veux dire, ne serait-il y aller de toute façon? Le C # code source de la méthode est juste une ligne, ce qui renvoie le résultat de la méthode.

Était-ce utile?

La solution

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, vous pouvez définir:

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

Définition d'un point d'arrêt sur l'accolade d'ouverture signifie « pause lorsque cette fonction appelée de get ». Voilà pourquoi il y a un au début de la méthode instruction no-op. Lorsque le point d'arrêt est fixé sur l'accolade d'ouverture le débogueur définit en fait sur le no-op.

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

return retVal;

dans

$retTmp = retVal;
goto exit;

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

exit:
return $ret;

En outre, en mode débogage, les compilateurs sont stupides sur le code qu'ils generation.They essentiellement faire quelque chose comme:

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

Dans votre cas, vous voyez:

return foo(arg);

être traduit 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 en mesure d'examiner ce code et se rendre compte qu'il y avait un certain redundency.However, les statisticiens ne font généralement que en mode débogage. Les optimisations du compilateur peuvent faire des choses comme éliminer les variables, et modifier l'ordre des instructions, ce qui rend le débogage difficile. Étant donné que le but d'une version de débogage est de permettre le débogage, il ne serait pas bon pour activer les optimisations.

Dans une version release, le code ne ressemblera pas à ça. 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);

Cela finit recherche assez simple.

Une chose à noter, cependant, est que je ne pense pas que le compilateur C # ne glisser beaucoup d'optimisations de fenêtres, même dans les versions de détail. C'est parce que la plupart de ces optimisations dépendent de l'architecture du processeur sous-jacent et sont donc effectuées par le compilateur JIT. Faire les optimisations, même celles qui sont le processeur agnostique, dans le compilateur C # peut nuire à la capacité du JIT d'optimiser le code (il cherche des modèles qui sont générés par la génération de code non optimisé, et si elle le juge fortement optimisé IL peut obtenir confus). Donc, en général compilateurs de code manged ne les font pas. Il fait quelques « choses coûteuses » (que le JIT ne veut pas faire lors de l'exécution), comme la détection de code mort, et l'analyse en direct variables, mais ils ne traitent pas les problèmes résolus en faisant glisser l'optimisation de la fenêtre.

Autres conseils

compilez vous en mode débogage ou libération? En mode de sortie je reçois:

.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 
}

Le Branching que vous voyez est probablement pour le soutien du débogueur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top