Pregunta “Se detectó un código inalcanzable” en MSIL o código nativo


¿El compilador compila "Códigos inalcanzables" en MSIL o código nativo en tiempo de ejecución?


5
2017-12-27 11:10


origen


Respuestas:


La pregunta no está clara, pero lo intentaré.

En primer lugar, la respuesta de Adam es correcta en la medida en que existe una diferencia en el IL que el compilador emite en función de si el interruptor "optimizar" está activado o desactivado. El compilador es mucho más agresivo en cuanto a eliminar código inalcanzable con el interruptor de optimización activado.

Hay dos tipos de código inalcanzable que son relevantes. Primero hay de jure código inalcanzable; es decir, el código que la especificación del lenguaje C # llama como inalcanzable. Segundo, hay de facto código inalcanzable; ese es el código que la especificación C # no menciona como inalcanzable, pero sin embargo, no se puede alcanzar. Del último tipo de código inalcanzable, hay un código que es conocido por el optimizador ser inalcanzable, y hay codigo desconocido para el optimizador ser inalcanzable

El compilador normalmente siempre elimina de jure Código inalcanzable, pero solo elimina de facto Código inalcanzable si el optimizador está encendido.

Aquí hay un ejemplo de cada uno:

int x = 123;
int y = 0;
if (false) Console.WriteLine(1);
if (x * 0 != 0) Console.WriteLine(2);
if (x * y != 0) Console.WriteLine(3);

Las tres Console.WriteLines son inalcanzables. El primero es de jure inalcanzable el compilador de C # indica que este código debe tratarse como inalcanzable a los fines de una verificación de asignación definida.

Los segundos dos son de jure accesible pero de facto inalcanzable Deben revisarse para detectar errores de asignación definidos, pero el optimizador puede eliminarlos.

De los dos, el optimizador detecta el caso (2) pero no el caso (3). El optimizador sabe que un entero multiplicado por cero siempre es cero, y que, por lo tanto, la condición siempre es falsa, por lo que elimina toda la declaración.

En el (3) caso, el optimizador no rastrea los posibles valores asignados a y y determina que y es siempre cero en el punto de la multiplicación. Aunque usted y yo sabemos que la consecuencia es inalcanzable, el optimizador no lo sabe.

El aspecto de la verificación de asignación definida es el siguiente: si tiene una declaración inalcanzable, todas las variables locales se consideran asignadas en esa instrucción, y se considera que no todas las asignaciones se producen:

int z;
if (false) z = 123;
Console.WriteLine(z); // Error
if (false) Console.WriteLine(z); // Legal

El primer uso es ilegal porque z no se ha asignado definitivamente cuando se usa. El segundo uso es no ilegal porque el código ni siquiera es accesible; z no puede usarse antes de que se le asigne porque el control nunca llega.

C # 2 tenía algunos errores donde confundía los dos tipos de accesibilidad. En C # 2 puedes hacer esto:

int x = 123;
int z;
if (x * 0 != 0) Console.WriteLine(z);

Y el compilador no se quejaría, aunque de jure la llamada a Console.WriteLine es accesible. Lo arreglé en C # 3.0 y tomamos el cambio radical.

Tenga en cuenta que nos reservamos el derecho de cambiar el funcionamiento del detector de código inalcanzable y el generador de código en cualquier momento; podemos decidir emitir siempre el código inalcanzable o nunca emitirlo o lo que sea.


13
2017-12-27 16:25



El compilador de C # puede compilar su aplicación en una configuración de depuración o una configuración de lanzamiento. Esto cambia el comportamiento de si el código inalcanzable se compila o no en CIL y se emite en el ejecutable de salida. Tomemos una función simple por ejemplo:

public static int GetAnswer()
{
    return 42;
    Console.WriteLine("Never getting here!");
}

Cuando compila esto en una configuración de depuración, el método completo (incluido el código inalcanzable) se emite como CIL. Se vería algo como esto (tal vez con algunos añadidos nop instrucciones para ayudar a la depuración):

.method public static int32 GetAnswer() cil managed
{
    .maxstack 1
    .locals init (int32)

            ldc.i4.s 42 // load the constant 42 onto the stack
            stloc.0 // pop that 42 from the stack and store it in a local
            br.s L_000C // jump to the code at L_000C

            ldstr "Never getting here!" // load the string on the stack
            call void [mscorlib]System.Console::WriteLine(string) // call method

    L_000C: ldloc.0 // push that 42 onto the stack from the local
            ret // return, popping the 42 from the stack
}

La razón por la que se emite todo este código es para que el depurador pueda permitirle a mano ingrese al código inalcanzable, tal vez para forzarlo a ejecutarse en circunstancias de depuración.

Dicho esto, cuando compile su proyecto bajo una configuración de lanzamiento, el compilador se dará cuenta de que, debido a que el ensamblaje construido no se incluirá en un depurador, no emitirá ningún código inalcanzable. El CIL se vería así:

.method public static int32 GetAnswer() cil managed
{
   .maxstack 1

    ldc.i4.s 42 // load the constant 42 onto the stack
    ret // return, popping the 42 from the stack
}

Simple, limpio y optimizado.


3
2017-12-27 11:59



En el modo de liberación, ciertos bloques de código pueden compilarse, pero eso no es lo que significa ese mensaje de error. Entonces, para responder a su pregunta, codifique como:

if (false)
 // do something

nunca lo convertiría en código de bytes a menos que tenga la depuración habilitada (eso se debe a que si adjunta un depurador, podría ingresar manualmente a esa instrucción if, por lo que el código debe estar allí).

Cuando recibe el mensaje de error de que la depuración no puede continuar debido al código inalcanzable, esto significa que el proceso que está depurando no se alinea con el código fuente que está utilizando (versiones diferentes, etc.).


0
2017-12-27 11:42