2009-09-30 9 views
6

Im przynosić IL z ILGenerator Oto mój kod: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); 

ten generowany ten IL:

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

(ja pobrać kod IL z VS Virtulizer nazwie ILStream)

Skąd kod nops? czy istnieje sposób, aby się ich pozbyć? Próbuję imitować kod C# i nie ma 3 nops.

+2

Być może jest to pakowanie instrukcji do bloku o określonej wielkości i wypełnianie reszty z nops? – Joe

+0

Ponieważ NOP nie wyrządza żadnej szkody, dlaczego chcesz się ich pozbyć? – RichardOD

+0

Jeśli nie ma sensu, dlaczego mają je tam, wynikowy kod C# nie ma żadnych ... – Peter

Odpowiedz

3

I rozwiązać problem poprzez odlanie int do wartości:

Kod:

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

Jesteś we właściwym kierunku, aby pozbyć się "NOP" S:

Kiedy Podajesz dodatkowy argument do wywołania Emit, zawsze upewnij się, że sprawdzasz w MSDN dla właściwego typu argumentu.

Dla OpCodes.Ldc_I4_S, MSDN stwierdza:

ldc.i4.s jest bardziej wydajne kodowanie za popychanie liczby całkowite od -128 do 127> na stosie oceny.

Następujące Emit przeciążenia metody można użyć opcodu ldc.i4.s:

ILGenerator.Emit (OpCode, bajt)

więc druga część kodu będzie miał nieprzewidywalne wyniki (oprócz tych brzydkich nopów) w czasie wykonywania, ponieważ próbujesz załadować "int8" na stosie, ale podajesz wartość "int32" lub "short":

Powinieneś użyć Ldc_I4 zamiast Ldc_I4_S, jeśli chcesz poprawnie załadować int32/short (lub coś większego niż bajt) na stos. więc kod powinien wyglądać tak zamiast powyższej próbki:

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

Jest to dzikie przypuszczenie, ale trzy NOP, że zostały wygenerowane mieć prawdopodobnie coś z dodatkowych bajtów z int32

Nadzieję, że pomaga ...

+2

Tak, gdy używasz przeciążenia int zamiast przeciążenia bajtowego, generator (na ślepo to wydaje) emituje argument jako 4-bajtowa liczba całkowita, gdy kod operacyjny oczekuje argumentu 1-bajtowego. Ponieważ jest emitowany w małym endianie, liczba jest wartością dodatnią mniejszą niż 128, a instrukcja nop jest pojedynczym bajtem "0x00", ma efekt dodania 3 instrukcji nop.Jeśli liczba całkowita byłaby 128, to załadowałaby 0 na stos, a pierwsza instrukcja nop byłaby zastąpiona instrukcją przerwania. –

+0

Nie powinno to być 'else if (IsBetween (value, sbyte.MinValue, sbyte.MaxValue)) gen.Emit (OpCodes.Ldc_I4_S, (short) value); else gen.Emit (OpCodes.Ldc_I4, value); ' – aboveyou00

+1

Prawidłowo, wziąłbym go jeszcze dalej i powiem, że powinien to być gen.Emit ((OpCodes.Ldc_I4_S, (byte) value), else ... Jednak , zauważ, że pierwszy przykład kodu jest przykładem tego, co jest nie tak, a nie co jest właściwe. –