2014-11-07 25 views
8

stavo scoprendo il IL code di un semplice programma:Qual è il significato di -2 in questa istruzione IL?

long x = 0; 
for(long i = 0;i< int.MaxValue * 2L; i++) 
{ 
    x = i; 
} 

Console.WriteLine(x); 

costruisco questo codice in uscita modalità e questo IL code viene generato:

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  28 (0x1c) 
    .maxstack 2 
    .locals init ([0] int64 x, 
      [1] int64 i) 
    IL_0000: ldc.i4.0 
    IL_0001: conv.i8 
    IL_0002: stloc.0 
    IL_0003: ldc.i4.0 
    IL_0004: conv.i8 
    IL_0005: stloc.1 
    IL_0006: br.s  IL_000f 
    IL_0008: ldloc.1 
    IL_0009: stloc.0 
    IL_000a: ldloc.1 
    IL_000b: ldc.i4.1 
    IL_000c: conv.i8 
    IL_000d: add 
    IL_000e: stloc.1 
    IL_000f: ldloc.1 
    IL_0010: ldc.i4.s -2 
    IL_0012: conv.u8 
    IL_0013: blt.s  IL_0008 
    IL_0015: ldloc.0 
    IL_0016: call  void [mscorlib]System.Console::WriteLine(int64) 
    IL_001b: ret 
} // end of method Program::Main 

io a capire praticamente tutto l'insructions eccetto questo:

IL_0010: ldc.i4.s -2 

Ora questo insruction shoul d spingere int.MaxValue * 2L nello stack e poi blt.s confronterà con i, se i è inferiore al valore di tornare alla IL_0008 .Ma, quello che non riesco a capire è per questo che lo carica -2? Se cambio il ciclo come di seguito:

for(long i = 0;i < int.MaxValue * 3L; i++) 
{ 
    x = i; 
} 

carica il valore atteso:

IL_0010: ldc.i8  0x17ffffffd 

Allora, qual è il significato di -2 in questo codice?

+5

È un'ottimizzazione, prendendo 3 byte di MSIL invece di 9. La costante -2 è un'ottimizzazione stessa, che prende 1 byte anziché 4. Si noti come la costante 0 non occupa affatto spazio, coperta da un opcode dedicato. –

risposta

13

int.MaxValue * 2L è un numero a 64 bit, che tuttavia si adatta comunque a 32 bit (4,294,967,294 o 0xFFFFFFFE). Quindi, il compilatore sta caricando 0xFFFFFFFE (che è uguale a -2 se interpretato come Int32) e quindi estendendolo a un valore a 64 bit senza segno.

Il motivo che ha utilizzato il modulo firmato è che il numero, quando interpretato come un valore con segno -2, si inserisce in un singolo byte firmata (-128 a 127), il che significa che il compilatore era in grado di emettere la forma breve ldc.i4.s opcode a caricare un valore a 32 bit da un singolo byte. Ci sono voluti solo 2 byte per caricare il numero intero con segno a 32 bit e 1 byte addizionale per convertirlo in un valore a 64 bit - questo è molto meglio dell'utilizzo di un'istruzione di caricamento a 64 bit seguita da un intero intero senza segno a 8 byte.

+0

Probabilmente prende questa "scorciatoia" per far compilare JIT più velocemente. Non tanto l'enfasi sul rendere leggibile IL;) Upvoted questa risposta in quanto è corretta, breve e concisa. – AlexanderBrevig

+0

@AlexanderBrevig: sì, il caricamento di una costante a 32 bit + codice d'accesso conv.u8 richiede meno spazio del caricamento di una costante a 64 bit, probabilmente questa è la logica. Come @Hans ha scritto sopra, l'opcode 'ldc.i4.s' richiede solo un singolo sbyte (firmato a 8 bit int) e lo estende in un valore a 32 bit con segno. – Groo

3

Sembra che il compilatore stia utilizzando la matematica bit a bit a proprio vantaggio. Si dà il caso che il valore di Two's Complement -2 è uguale al valore senza segno interi di (int.MaxValue * 2L)

Nella rappresentazione bitwise:

-           1111 1111 1111 1111 1111 1111 1111 1110 (int) 
-           1111 1111 1111 1111 1111 1111 1111 1110 (uint) 
- 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1111 1110 (long