在IL code,为什么会出现不NOP运算code在特定情况下?为什么会出现br.s运code在特定情况下?在特定情况下、code、IL、br

2023-09-03 07:03:48 作者:本尊拒绝爱

假设我有以下的code:

 公共类的Class1
{
    私人的Class2 OBJ;

    公共无效方法a()
    {
        VAR类2 =新的Class2();
        class2.PropertyI = 2;
        OBJ =方法b(等级2);
    }

    公众的Class2方法b(等级2等级2)
    {
        返回Class2的;
    }
}

公共类的Class2
{
    公众诠释PropertyI {获得;组; }
}
 

生成的IL $ C $从使用Visual Studio 2010作为.NET 2.0的程序集编译C如下:

 。方法公开hidebysig实例无效治法()CIL管理
{
    .maxstack 3
    .locals的init(
        [0]类ClassLibrary1.Class2类2)
    L_0000:NOP
    L_0001:newobj实例无效ClassLibrary1.Class2 ::构造函数()。
    L_0006:stloc.0
    L_0007:ldloc.0
    L_0008:ldc.i4.2
    L_0009:callvirt实例无效ClassLibrary1.Class2 :: set_PropertyI(INT32)
    L_000e:NOP
    L_000f:ldarg.0
    L_0010:ldarg.0
    L_0011:ldloc.0
    L_0012:调用实例类ClassLibrary1.Class2 ClassLibrary1.Class1 ::方法b(类ClassLibrary1.Class2)
    L_0017:stfld类ClassLibrary1.Class2 ClassLibrary1.Class1 :: OBJ
    L_001c:RET
}

。方法公开hidebysig实例类ClassLibrary1.Class2方法b(类ClassLibrary1.Class2等级2)CIL管理
{
    .maxstack 1
    .locals的init(
        [0]类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
}
 

我的问题有以下几种:

在治法,为什么会出现不是 NOP code之间 L_0006 L_0007 ? 由于 L_0001 L_0006 L_0007 不同的以 L_0009 ,为什么没有 NOP 运算code? 在方法b,为什么 L_0003 有必要吗? 解决方案

C#编译器发出的一个大括号NOP指令。这使得它的很多的更容易设置在code断点。调试器只允许设置在code和一个大括号通常不会产生任何code断点。所以,这只是一个简单的调试援助,这些NOP指令将不会在发布版本获取生成。

该BR.S指令是在编译器中一个小小的瑕疵,它没有一个窥孔优化获得摆脱这类外来指令。一般情况下,它不是C#的编译器的工作,以优化code,这是由the抖动。这很容易和方便地取出指令。

DotNet并行计算的使用误区 二

Suppose I have the following code:

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; }
}

The generated IL code from compiling with Visual Studio 2010 as a .NET 2.0 assembly is the following:

.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 
}

My questions are the following:

In MethodA, why is there not a nop code between L_0006 and L_0007? Since L_0001 to L_0006 are distinct from L_0007 to L_0009, why is there no nop opcode? In MethodB, why is L_0003 necessary?

解决方案

The C# compiler emits a NOP instruction at a curly brace. Which makes it a lot easier to set breakpoints in your code. The debugger only permits setting a breakpoint on code and a curly brace doesn't normally produce any code. So this is just a simple debugging aid, these NOPs won't get generated in the release build.

The BR.S instruction is a minor flaw in the compiler, it doesn't have a peephole optimizer to get rid of these kind of extraneous instructions. In general, it is not the job of the C# compiler to optimize code, that's done by the jitter. Which will readily and easily remove the instruction.