未优化的 MSIL 中多余的 NOP 和分支 [重复]

Posted

技术标签:

【中文标题】未优化的 MSIL 中多余的 NOP 和分支 [重复]【英文标题】:Superfluous NOPs and branches in unoptimized MSIL [duplicate] 【发布时间】:2016-04-18 07:58:31 【问题描述】:

当我将以下代码编译为调试时...

public class MyClass

    private int myField;

    public int MyProperty
    
        get  return myField; 
        set  myField = value; 
    

...编译器生成带有看似无用指令的奇怪字节码。比如看看属性MyProperty的getter生成了什么(用ildasm.exe反汇编):

.method public hidebysig specialname instance int32 
        get_MyProperty() cil managed

    // Code size       12 (0xc)
    .maxstack  1
    .locals init ([0] int32 CS$1$0000)
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  ldfld      int32 MSILTest.MyClass::myField
    IL_0007:  stloc.0
    IL_0008:  br.s       IL_000a
    IL_000a:  ldloc.0
    IL_000b:  ret
 // end of method MyClass::get_MyProperty

具体来说,nopIL_0000 在那里做什么?为什么编译器会在IL_0008 处生成这条无用的br.s 指令?为什么会创建临时局部变量CS$1$0000

对于发布配置,指令集按预期生成:

IL_0000:  ldarg.0
IL_0001:  ldfld      int32 MSILTest.MyClass::myField
IL_0006:  ret

编辑

我想我已经找到了为什么分支和临时局部变量存在于different question 中的问题的答案:这可能是为了在调试期间轻松设置断点。所以剩下的问题是为什么会生成nop 指令。

【问题讨论】:

未优化的代码未优化。 NOP 是调试器断点的目标。删除由递归体面的解析器产生的冗余负载和存储需要一个窥孔优化器。消除不必要的本地人也是一项优化工作。 jitter 本身可以在没有编译器帮助的情况下处理这些优化工作。 另见Subtleties of C# IL codegen,谷歌第一次点击“C# debug nop”:“关闭优化后,C# 编译器会在所有地方发出 no-op IL 指令。带有调试信息打开和关闭优化,其中一些无操作将成为语句或表达式片段的断点目标,否则很难在其上放置断点“. 【参考方案1】:

编译器创建这些操作码是为了提供更好的调试体验。在每个 c# 行的开头,编译器将引入 nop 所以调试器可以在那里中断。发出分支和临时局部变量,让您分析函数的返回值。

【讨论】:

以上是关于未优化的 MSIL 中多余的 NOP 和分支 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

MSIL 教程:数组分支循环使用不安全代码和如何调用Win32 API(转)

MSIL 教程:类和异常处理(转)

git 同步远程已删除的分支和删除本地多余的分支

git同步远程已删除的分支和删除本地多余的分支

如何在 Azure DevOps 中更改分支名称

git:使用分支