为什么' br.s'在这种情况下使用IL操作码?

时间:2014-10-12 08:47:07

标签: c# il

出于教育目的,我学习了一点IL(主要是因为我很好奇会发生什么事情'%'引擎盖下(原来是rem)并开始离题... )。

我写了一个方法,只是返回true来解决一些问题并且想知道' br.s'操作码:

.method public hidebysig static bool  ReturnTrue() cil managed
{
  // Code size       7 (0x7)
  .maxstack  1
  .locals init ([0] bool CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
  IL_0003:  br.s       IL_0005
  IL_0005:  ldloc.0
  IL_0006:  ret
} // End of method Primes::ReturnTrue

ldc.i4.1在堆栈上推送1并且stloc.0将其放在第0个本地,br.s基本上(据我所知)做了一个' goto'到IL_0005行的ldloc.0。

这是为什么?为什么没有IL_0004线因此可以省略?

3 个答案:

答案 0 :(得分:15)

该分支用于调试目的,已经计算并存储了返回值,现在可以“调用”调试器。它与方法条目中的NOP相同。

关于IL_0004,作为@hvd状态,br.s有一个地址,不适合“一行”,这里有一个字节(我不知道你有多熟悉寻址,但一条指令通常是一个字节,即8位,以及地址或偏移量,通常是8位,16位或32位。在这种情况下,我们有一个8位操作码和8位位偏移。Wikipedia has a good article on CIL-OP-codes)。

另外,假设您的方法有多个返回并通过例如if - 分支,它们都会跳到最后,IL_0005在您的情况下,因此在函数返回时只需要一个断点

答案 1 :(得分:5)

这是recursive-descent parser的一个非常常见的工件,就像C#编译器使用的工件一样。摆脱这些分支需要peephole optimizer

当编译器本身优化了一个简单的操作时,可能会发生,其结果可以在编译时确定。 C#编译器没有窥视孔优化器,因为它不是必需的,抖动负责消除这些不必要的分支。将优化器置于抖动中通常是一种成功的策略,每个语言编译器都从中受益。保持编译器非常简单,并且在一个(或几个)位置编写和维护代码优化器的费用相当可观。

这不是抖动优化器结束的地方,整个方法将在运行时消失。由于您的方法的返回值在编译时是已知的,因此任何调用该方法的代码都将大大优化,这很可能。看到这种MSIL是一个强烈的暗示,你的代码很容易被简化或有错误:)

答案 2 :(得分:0)

在Visual Studio 2013上不会发生这种情况,似乎dev终于修复了它。 它在VS2013上看起来像这样。

.method public hidebysig static bool  ReturnTrue() cil managed
{
  .maxstack  1
  ldc.i4.1
  ret
} // End of method Primes::ReturnTrue