2014-10-12 170 views
15

为了教育目的,我学习了一点IL(主要是因为我很好奇底层的'%'会发生什么(原来是REM)并开始离题了......)。为什么在这种情况下使用'br.s'IL操作码?

我写了一个方法,就返回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后栈和stloc上推1 .0将它放置在第0个局部,br.s基本上(据我所知)在IL_0005行做了'goto'到ldloc.0。

这是为什么?为什么没有IL_0004线,所以这可以省略?

+6

没有IL_0004,因为'br.s'是一个双字节指令。我猜你禁用了优化?是的,然后期待奇怪的事情。当你开启优化时你会得到什么? – hvd 2014-10-12 08:51:38

+1

然后它只是推动'1'到堆栈并立即返回。 IL_0000:ldc.i4.1 IL_0001:ret – Apeiron 2014-10-12 08:57:21

+0

在“IL_xxxx”中,xxxx是指令从字节开始偏移的偏移量。 br.s是一个单字节指令(与其余部分一样),它需要一个单字节操作数,这是要跳转到的目标指令。 – 2014-10-12 09:13:23

回答

13

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

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

此外,假设你的方法有多种收益,并通过例如if -branches,所有的人都跳到结束,IL_0005你的情况,因此需要在函数返回只有一个断点。

+0

咦?这是字节数,对于'br.s'操作数只有一个字节,并且地址通常不适合一个字节。 'BR。s'需要一个8位的相对偏移量,而不是一个地址。 – hvd 2014-10-12 09:18:55

+0

@hvd你是对的,我一直假定IL地址不是基于字节的,而是基于字长的,但是你是正确的先生;) – flindeberg 2014-10-12 09:24:34

3

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

当编译器自己优化了一个平凡的操作,其结果可以在编译时确定时,可能会发生。 C#编译器没有窥视孔优化器,因为它不是必需的,抖动会消除这些不必要的分支。把优化器放在抖动中通常是一个胜利的策略,每个语言编译器都从中受益。保持编译器非常简单,并且在一个(或几个)地方编写和维护代码优化器的成本很高。

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

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 
+1

尝试使用'debug'-flag set进行编译。 – flindeberg 2016-04-20 11:55:11

相关问题