在IL代码中,为什么在给定的情况下没有nop操作码?为什么在给定的情况下有一个br.s操作码?

cm0*_*007 7 .net cil opcode .net-2.0

假设我有以下代码:

public class Class1
{
    private Class2 obj;

    public void MethodA()
    {
        var class2 = new Class2();
        class2.PropertyI = 2;
        obj = MethodB(class2);
    }

    public Class2 MethodB(Class2 class2)
    {
        return class2;
    }
}

public class Class2
{
    public int PropertyI { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

使用Visual Studio 2010作为.NET 2.0程序集进行编译生成的IL代码如下:

.method public hidebysig instance void MethodA() cil managed
{
    .maxstack 3
    .locals init (
        [0] class ClassLibrary1.Class2 class2)
    L_0000: nop 
    L_0001: newobj instance void ClassLibrary1.Class2::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldc.i4.2 
    L_0009: callvirt instance void ClassLibrary1.Class2::set_PropertyI(int32)
    L_000e: nop 
    L_000f: ldarg.0 
    L_0010: ldarg.0 
    L_0011: ldloc.0 
    L_0012: call instance class ClassLibrary1.Class2 ClassLibrary1.Class1::MethodB(class ClassLibrary1.Class2)
    L_0017: stfld class ClassLibrary1.Class2 ClassLibrary1.Class1::obj
    L_001c: ret 
}

.method public hidebysig instance class ClassLibrary1.Class2 MethodB(class ClassLibrary1.Class2 class2) cil managed
{
    .maxstack 1
    .locals init (
        [0] class ClassLibrary1.Class2 CS$1$0000)
    L_0000: nop 
    L_0001: ldarg.1 
    L_0002: stloc.0 
    L_0003: br.s L_0005
    L_0005: ldloc.0 
    L_0006: ret 
}
Run Code Online (Sandbox Code Playgroud)

我的问题如下:

  1. 在MethodA中,为什么nopL_0006和之间没有代码L_0007
    • 由于L_0001L_0006来自不同的L_0007L_0009,为什么没有nop操作码?
  2. 在MethodB中,为什么有L_0003必要?

Han*_*ant 10

C#编译器在大括号中发出NOP指令.这使得它很多更容易在代码中设置断点.调试器只允许在代码上设置断点,大括号通常不会产生任何代码.所以这只是一个简单的调试辅助工具,这些NOP不会在发布版本中生成.

BR.S指令是编译器中的一个小缺陷,它没有窥视孔优化器来摆脱这些无关的指令.通常,优化代码不是C#编译器的工作,而是由抖动完成.哪个会轻松轻松地删除指令.

  • L_0006没有大括号。与不需要BR.S的方式相同,不需要L_000e。真的没关系,这不一定是完美的。错误需要修复时才得到修复,而不是因为它们存在。从递归的体面解析器中消除虚假代码生成可能很困难,并且修复它并不总是值得将其破坏的风险。如果要查找原因,则可以从SSCLI20发行版csharp / sccomp子目录中研究C#编译器的源代码。 (2认同)