8

我发现了IL code一个简单的程序:

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

Console.WriteLine(x);

我在发布模式下构建此代码并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

我弄清楚了几乎所有的指令,除了这个:

 IL_0010:  ldc.i4.s   -2

现在这个指令应该推入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这段代码中的含义是什么?

4

2 回答 2

13

int.MaxValue * 2L是一个 64 位数字,但仍适合 32 位(4,294,967,2940xFFFFFFFE)。因此,编译器正在加载0xFFFFFFFE(解释为时等于 -2 Int32),然后将其扩展为无符号 64 位值。

它使用有符号形式的原因是,当解释为有符号值时,该数字-2适合单个有符号字节(-128to 127),这意味着编译器能够发出短格式ldc.i4.s操作码以从单字节。加载 32 位有符号整数只需要 2 个字节,另外需要 1 个字节将其转换为 64 位值 - 这比使用 64 位加载指令后跟完整的 8 字节无符号整数要好得多。

于 2014-11-07T13:32:58.640 回答
3

看起来编译器正在使用按位数学来发挥其优势。碰巧-2 的二进制码值等于 (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
于 2014-11-07T13:36:30.210 回答