2009-09-30 6 views
6

Im générer une IL avec le ILGenerator voici mon code: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); 

Cela a généré cette IL:

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

(je reçois le code IL d'un VS Virtulizer nommé ILStream)

D'où le code nops? est-il possible de s'en débarrasser? Im essayant d'imiter du code C# et il n'a pas 3 nops.

+2

Peut-être que c'est emballer les instructions dans un bloc d'une certaine taille et remplir le reste avec des nops? – Joe

+0

Comme le NOP ne fait aucun mal, pourquoi souhaitez-vous vous en débarrasser? – RichardOD

+0

Si elles ne servent à rien pourquoi les avoir là, le code C# résultant n'en a pas ... – Peter

Répondre

3

je résolu le problème en jetant l'int à une valeur:

code:

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

Vous êtes dans la bonne direction pour se débarrasser des s "NOP":

Lorsque vous fournissez un argument supplémentaire à un appel Emit, assurez-vous toujours de vérifier sur MSDN le type d'argument approprié.

Pour OpCodes.Ldc_I4_S, MSDN indique:

ldc.i4.s est un codage plus efficace pour pousser les nombres entiers de -128 à 127 sur la pile d'évaluation>.

La surcharge de méthode Emit suivante peut utiliser l'opcode ldc.i4.s:

ILGenerator.Emit (OpCode, octet)

Ainsi, la deuxième partie de votre code aura des résultats imprévisibles (en dehors de ces satanés) à l'exécution, puisque vous essayez de charger un "int8" sur la pile, mais en fournissant une valeur "int32" ou "short":

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

Vous devez utiliser Ldc_I4 au lieu de Ldc_I4_S si vous voulez charger correctement un int32/short (ou n'importe quoi d'une plus grande magnitude qu'un octet) sur la pile. Donc, votre code devrait ressembler à ceci au lieu de l'exemple ci-dessus:

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

Ceci est une supposition sauvage, mais les trois années NOP qui ont été générés sans doute quelque chose à faire avec les octets supplémentaires de votre int32

Espérons que cela aide ...

+2

Oui, quand vous utilisez la surcharge int au lieu de la surcharge d'octets, le générateur (aveuglément il semblerait) émet l'argument comme un entier de 4 octets lorsque l'opcode attend un argument de 1 octet. Parce qu'il est émis en petit boutiste, le nombre est une valeur positive inférieure à 128 et l'instruction nop se trouve être un seul octet '0x00', cela a pour effet d'ajouter 3 instructions nop.Si l'entier est passé à 128 alors il aurait chargé 0 sur la pile et la première instruction nop serait remplacée par une instruction de rupture. –

+0

Cela ne devrait pas être 'else if (IsBetween (valeur, sbyte.MinValue, sbyte.MaxValue)) gen.Emit (OpCodes.Ldc_I4_S, valeur (courte)); else gen.Emit (OpCodes.Ldc_I4, value); ' – aboveyou00

+1

Correct, je serais même aller plus loin et dire qu'il devrait être gen.Emit ((OpCodes.Ldc_I4_S, (byte) valeur); sinon ... Cependant , notez que le premier exemple de code est l'exemple de ce qui ne va pas, pas de ce qui est juste. –