2009-09-30 9 views
6

Im generando una certa IL con l'ILGenerator qui è il mio codice: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); 

Ciò ha generato questa IL:

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

(ottengo il codice IL da un VS Virtulizer chiamato ILStream)

Da dove viene il codice di sicurezza? c'è un modo per sbarazzarsi di loro? Sto cercando di imitare qualche codice C# e non ha 3 nops.

+2

Forse sta impacchettando le istruzioni in un blocco di una certa dimensione e riempendo il resto con i nops? – Joe

+0

Come il NOP non fa male, perché vuoi liberarti di loro? – RichardOD

+0

Se non ci sono motivi per cui sono lì, il codice C# risultante non ha alcun ... – Peter

risposta

3

ho risolto il problema lanciando l'int a un valore:

Codice:

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

Sei nella giusta direzione per sbarazzarsi dei "NOP" s:

Quando fornisci un argomento aggiuntivo per una chiamata Emit, assicurati sempre di verificare su MSDN il tipo di argomento appropriato.

Per OpCodes.Ldc_I4_S, MSDN afferma:

ldc.i4.s è una codifica più efficiente per la spinta degli interi da -128 a 127 sul> stack di valutazione.

Il seguente overload del metodo Emit è possibile utilizzare il codice operativo ldc.i4.s:

ILGenerator.Emit (OpCode, byte)

Quindi la seconda parte del codice avrà risultati imprevedibili (oltre a quelli fastidiosi nop) in fase di esecuzione, poiché si sta tentando di caricare un "int8" nello stack, ma fornendo un valore "int32" o "breve":

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

È necessario utilizzare Ldc_I4 anziché Ldc_I4_S se si desidera caricare correttamente uno stack int32/short (o qualcosa di maggiore di un byte). Quindi il codice dovrebbe essere simile al posto del campione di cui sopra:

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

Questo è un ipotesi, ma i tre NOP di che sono stati generati probabilmente hanno qualcosa a che fare con i byte in più dal vostro int32

Spero che sia d'aiuto ...

+2

Sì, quando si usa il sovraccarico int invece del sovraccarico di byte, il generatore (ciecamente sembrerebbe) emette l'argomento come un intero di 4 byte quando l'opcode si aspetta un argomento da 1 byte. Poiché viene emesso in little endian, il numero è un valore positivo inferiore a 128 e l'istruzione nop è un singolo byte "0x00", ha l'effetto di aggiungere 3 istruzioni nop.Se il numero intero fosse 128, avrebbe caricato 0 nello stack e la prima istruzione nop sarebbe stata sostituita con un'istruzione break. –

+0

Non dovrebbe essere "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

Corretto, lo farei ulteriormente e dire che dovrebbe essere gen.Emit ((OpCodes.Ldc_I4_S, (byte) value), altrimenti ... Tuttavia , tieni presente che il primo esempio di codice è l'esempio di ciò che è sbagliato, non di ciò che è giusto. –

Problemi correlati