2014-11-07 4 views
8

я обнаруживал в IL code простой программы:Каково значение -2 в этой инструкции IL?

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

Console.WriteLine(x); 

я строй этого кода в Release режиме и этот IL code генерируются:

.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 

выяснит довольно много всех insructions кроме это:

IL_0010: ldc.i4.s -2 

Сейчас эта застройка shoul d нажать int.MaxValue * 2L в стек, а затем blt.s будет сравнить его с i, если i меньше значения вернуться к IL_0008., что я не могу понять, что, почему он загружает -2? Если изменить цикл, как показано ниже:

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

Он загружает ожидаемое значение:

IL_0010: ldc.i8  0x17ffffffd 

Так что есть смысл -2 в этом коде?

+5

Это является оптимизация, принимая 3 байта MSIL вместо 9. Константа -2 - это сама оптимизация, принимающая 1 байт вместо 4. Обратите внимание, как константа 0 не занимает места вообще, покрывается специальным кодом операции. –

ответ

13

int.MaxValue * 2L - это 64-разрядное число, которое, однако, все еще вписывается в 32-разрядные (4,294,967,294, или 0xFFFFFFFE). Таким образом, компилятор загружает 0xFFFFFFFE (который равен -2 при интерпретации Int32), а затем расширяет его до 64-битного значения без знака.

Причина, по которой используется подписанная форма является то, что число, когда интерпретируется как знаковое значение -2, помещается в один байт со знаком (-128 к 127), а это означает, что компилятор был способен излучать короткую форму ldc.i4.s опкод к загрузите 32-битное значение из одного байта. Для загрузки 32-разрядного знакового целого числа потребовалось всего 2 байта и еще 1 байт, чтобы преобразовать его в 64-битное значение - это намного лучше, чем использование 64-разрядной команды загрузки, за которой следует полное 8-байтовое целое без знака.

+0

Вероятно, этот «ярлык» позволяет сделать компиляцию JIT быстрее. Не так много внимания уделяется тому, чтобы сделать IL удобочитаемым;) Подтвердил этот ответ как правильный, короткий и лаконичный. – AlexanderBrevig

+0

@AlexanderBrevig: да, загрузка 32-битного константного кода + conv.u8 занимает меньше места, чем загрузка 64-битной константы, возможно, это и есть логическое обоснование. Как писал @Hans, код операции 'ldc.i4.s' требует только одного параметра sbyte (подписанного 8-битного int) и расширяет его до 32-битного значения. – Groo

3

Похоже, что компилятор использует побитную математику в своих интересах. Просто так получилось, что значение -2 Two's Complement равно целых чисел без знака значения (int.MaxValue * 2L)

В представлении побитового:

-           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 
Смежные вопросы