为什么编译器创建的指令在从方法返回字符串时似乎什么都不做

本文关键字:什么 字符串 返回 创建 编译器 指令 方法 为什么 | 更新日期: 2023-09-27 18:22:15

我正在查看为一个非常简单的方法生成的IL,因为我想自己做一点反射发射,我遇到了这个问题的评论中提到的东西(但不是问题):使用Br_S OpCode使用reflection.Emit.Label指向下一条指令,但没有人回答,我对此感到好奇。所以…

如果我有这样的方法:

    public string Test()
    {            
        return "hello";
    }

然后我在上面运行ILDASM,我看到IL是这样的:

.method public hidebysig instance string 
        Test() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "hello"
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
}

我感到好奇的部分是:

  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0

第一行是无条件转移到第二行。为什么要做这个手术,难道它没有任何作用吗?

编辑

我的问题似乎措辞不当,因为我想知道的内容有些混乱。最后一句话应该是这样的:

编译器输出这个无条件传输语句的原因是什么

更新

这是一个断点的建议让我考虑在发布模式下尝试编译它,果不其然,我感兴趣的部分消失了,IL变成了这个(这就是为什么我突然认为断点答案就是原因):

.method public hidebysig instance string 
        Test() cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldstr      "hello"
  IL_0005:  ret
} 

尽管如此,"为什么它在那里"的问题仍然萦绕在我的脑海中——如果这不是编译器始终的工作方式,而且不是出于一些有用的调试原因(比如有一个放置断点的地方),为什么要使用它?

我想答案可能是:"只是它的制作方式,没有充分的理由,这并不重要,因为JIT最终会很好地解决所有问题。"

我希望我现在没有问这个,这会毁了我的接受率!!:-)

为什么编译器创建的指令在从方法返回字符串时似乎什么都不做

两条指令中的第一条是return语句的标准代码的一部分,第二条指令是该方法的样板代码的一部份。

return语句将返回值放入一个局部变量中,然后跳到方法的出口点:

IL_0001:  ldstr      "hello"
IL_0006:  stloc.0
IL_0007:  br.s       IL_0009

该方法的样板代码从局部变量获得返回值,然后退出该方法:

IL_0009:  ldloc.0
IL_000a:  ret

在编译器创建的IL代码中,方法总是有一个单独的出口点。这就是为什么return语句会跳转到那个位置,而不是直接退出函数。return语句的代码总是相同的,因此即使它跳到下一条指令,也总是有一个分支。

编译器通常会生成看起来效率低下的IL代码,因为JIT编译器会优化代码。编译器生成未优化、简单且可预测的代码,JIT编译器更容易进行优化。

NOP指令在调试中的一个原因是构建它以使您能够设置断点。

VB.net内部,深入到调试

Visual Basic.NET允许您在未执行的代码行(如End If、End Sub和Dim语句)上设置断点。为了促进这种流行的调试技术,编译器将nop指令作为非执行代码行的占位符插入(因为非执行行不会转换为IL指令)。nop指令是一条"无操作"指令——它不执行任何有意义的工作,但可能会消耗一个处理周期。

在互联网上快速浏览表明,它可能是return语句的代码。它应该将代码返回到函数的末尾,并且必须在函数体的任何位置工作。我猜它可以优化,但你没有提到代码的优化级别。

在中间语言中,当您看到指令"br"时表示分支。分支指令是条件指令将根据条件在C#语言中,诸如goto、while、for、break、,和return可以用分支的变体实现指示

http://www.dotnetperls.com/il