Pregunta

Puede alguien explicar estas pocas líneas de MSIL? ¿Por qué se mueven un valor fuera de la pila de evaluación a una variable local, sólo para devolverlo inmediatamente y devolverlo?

El siguiente código MSIL carga un solo argumento (una cadena), llama a un método, que devuelve bool y, a continuación, devuelve ese valor bool. Lo que no entiendo es por qué se llama stloc.0 para almacenar el valor devuelto por el método en una variable local, a continuación, realiza una transferencia de control expresa con la línea siguiente etiqueta (parece innecesario), sólo para mover el valor de la derecha de nuevo en la pila de evaluación antes de devolverlo.

.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 

Mi mejor conjetura por qué hace esto es para llevar a cabo algún tipo de comprobación de tipos para asegurar el valor en la pila de evaluación es en realidad un valor booleano antes de devolverlo. Pero estoy desorientado sobre el salto explícita a la siguiente línea; Quiero decir, ¿no ir allí de todos modos? El código fuente C # para el método es sólo una línea, que devuelve el resultado del método.

¿Fue útil?

Solución

Si abre esta función en el depurador, con el código compilado en modo de depuración:

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

Hay 3 puntos de ruptura se puede establecer:

  1. En la llave de apertura de la función.
  2. En la línea de "retorno".
  3. En la llave de cierre de la función.

Configuración de un punto de ruptura en la llave de apertura significa "descanso cuando esta función de llamada get". Es por eso que hay una instrucción de no operación a principios del método. Cuando el punto de interrupción se establece en la llave de apertura del depurador en realidad pone sobre la no-op.

Configuración de un punto de ruptura en la llave de cierre significa "descanso cuando esta función salidas". Para que esto suceda la función tiene que tener una sola instrucción de retorno en el que es IL, donde el punto de interrupción se puede poner. El compilador permite que mediante el uso de una variable temporal para almacenar el valor de retorno, y la conversión

return retVal;

en

$retTmp = retVal;
goto exit;

y luego inyectar el siguiente código en la parte inferior del método:

exit:
return $ret;

Además, cuando se encuentra en modo de depuración, compiladores son estúpidos sobre el código que básicamente generation.They hacer algo como:

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

En su caso, que está viendo:

return foo(arg);

siendo traducido a:

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

Si el compilador estaba haciendo una "optimización de ventana deslizante" que podría ser capaz de mirar a ese código y darse cuenta de que había algo de redundency.However, compiladores lo general no hacen eso en el modo de depuración. optimizaciones del compilador puede hacer cosas como eliminar las variables y reordenar las instrucciones, lo que hace difícil la depuración. Puesto que el propósito de una versión de depuración es para habilitar la depuración, no sería bueno para encender optimizaciones.

En una versión de lanzamiento, el código no se verá así. Esto se debe a que el compilador no introduce el código especial para permitir puntos de interrupción en las llaves de apertura y cierre, que sólo deja la siguiente para ser compilado:

return bar(arg);

que termina pareciendo bastante simple.

Hay que destacar, sin embargo, es que no creo que el compilador de C # no deslizante tanto optimizaciones de ventanas, incluso en versiones comerciales. Eso es porque la mayoría de esas optimizaciones dependen de la arquitectura de procesador subyacente y así se hacen por el compilador JIT. Hacer las optimizaciones, incluso los que son procesador agnóstica, en el compilador de C # puede impedir la capacidad del equipo conjunto de investigación para optimizar el código (que está en busca de patrones que se generan por la generación no optimizado código, y si se ve muy optimizado IL se puede conseguir confuso). compiladores de código por lo general manejadas no lo hacen ellos. Hace algunas "cosas caras" (que el JIT no quiere hacer en tiempo de ejecución), como la detección de código muerto, y el análisis de las variables en vivo, pero no se ocupan de los problemas resueltos por deslizamiento optimización ventana.

Otros consejos

¿Está compilando en modo de depuración o liberación? En el modo de disparo consigo:

.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 ramificación que se está viendo es, probablemente, para el apoyo depurador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top