2009-09-30 11 views
6

Im generar alguna IL con el ILGenerator aquí está mi código:C# ILGenerator nop?

DynamicMethod method = new DynamicMethod("test", null, Type.EmptyTypes); 
ILGenerator gen = method.GetILGenerator(); 
gen.Emit(OpCodes.Ldarg_0); 
gen.Emit(OpCodes.Ldarg_1); 
gen.Emit(OpCodes.Ldc_I4_S, 100); 

Esto generó esta IL:

IL_0000: ldarg.0  
IL_0001: ldarg.1  
IL_0002: ldc.i4.s 100 
IL_0004: nop   
IL_0005: nop   
IL_0006: nop   

(I obtener el código IL desde un Virtulizer VS llamado ILStream)

¿De dónde codifican los nops? ¿hay alguna manera de deshacerse de ellos? Im tratando de imitar algún código C# y no tiene 3 nudos.

+2

¿Quizás está empaquetando las instrucciones en un bloque de cierto tamaño y llenando el resto con nops? – Joe

+0

Como el NOP no causa ningún daño, ¿por qué desea deshacerse de ellos? – RichardOD

+0

Si no sirven, por qué tenerlos allí, el código C# resultante no tiene ninguno ... – Peter

Respuesta

3

He resuelto el problema echando el int a un valor:

Código:

private static bool IsBetween(int value, int min, int max) 
{ 
    return (value >= min && value <= max); 
} 

private static void WriteInt(ILGenerator gen, int value) 
{ 
    gen.Emit(OpCodes.Ldarg_1); 
    if (IsBetween(value, sbyte.MinValue, sbyte.MaxValue)) 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, (sbyte)value); 
    } 
    else if (IsBetween(value, byte.MinValue, byte.MaxValue)) 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, (byte)value); 
    } 
    else if (IsBetween(value, short.MinValue, short.MaxValue)) 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, (short)value); 
    } 
    else 
    { 
     gen.Emit(OpCodes.Ldc_I4_S, value); 
    } 
} 
8

Usted está en la dirección correcta para deshacerse de los "nop" s:

Cuando proporciona un argumento adicional para una llamada de Emitir, siempre asegúrese de comprobar en MSDN el tipo de argumento adecuado.

Para OpCodes.Ldc_I4_S, MSDN afirma:

ldc.i4.s es una codificación más eficiente para empujar los números enteros de -128 a 127 en la> pila de evaluación.

La siguiente sobrecarga del método Emit puede utilizar el código de operación ldc.i4.s:

ILGenerator.Emit (código de operación, byte)

Así que la segunda parte de su código tendrá resultados impredecibles (además de esos fastidiosos nop) en tiempo de ejecución, ya que está intentando cargar un "int8" en la pila, pero proporcionando un valor "int32" o "corto":

else if (IsBetween(value, short.MinValue, short.MaxValue)) 
{ 
    gen.Emit(OpCodes.Ldc_I4_S, (short)value); 
} 
else 
{ 
    gen.Emit(OpCodes.Ldc_I4_S, value); 
} 

Debe usar Ldc_I4 en lugar de Ldc_I4_S si desea cargar correctamente un int32/short (o algo de mayor magnitud que un byte) en la pila. lo que el código debería tener este aspecto en lugar de la muestra anterior:

else 
{ 
    gen.Emit(OpCodes.Ldc_I4, value); 
} 

Esta es una suposición, pero los tres de nop que se generaron probable que tenga algo que ver con los bytes adicionales de su int32

Espero que ayude ...

+2

Sí, cuando se usa la sobrecarga int en lugar de la sobrecarga de bytes, el generador (a ciegas parecería) emite el argumento como un entero de 4 bytes cuando el código de operación espera un argumento de 1 byte. Debido a que se está emitiendo en little endian, el número es un valor positivo menor que 128 y la instrucción nop es un byte '0x00', tiene el efecto de agregar 3 instrucciones de nop.Si el número entero fuera 128, entonces habría cargado 0 en la pila y la primera instrucción nop se reemplazaría por una instrucción break. –

+0

No debería ser 'else if (IsBetween (valor, sbyte.MinValue, sbyte.MaxValue)) gen.Emit (OpCodes.Ldc_I4_S, valor (corto)); else gen.Emit (OpCodes.Ldc_I4, value); ' – aboveyou00

+1

Correcto, incluso lo tomaría más y diría que debería ser gen.Emit ((OpCodes.Ldc_I4_S, (byte) value); else ... Sin embargo , tenga en cuenta que la primera muestra de código es el ejemplo de lo que está mal, no lo que es correcto. –

Cuestiones relacionadas