2013-03-22 77 views
6

假設我有以下代碼:在IL代碼中,爲什麼在給定的情況下沒有nop操作碼?爲什麼在給定的情況下有br.s操作碼?

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

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

我的問題有以下幾種:

  1. 在治法,爲什麼會出現不L_0006L_0007之間的nop代碼?
    • 由於L_0001L_0006L_0007L_0009不同,爲什麼沒有nop操作碼?
  2. 在MethodB中,爲什麼需要L_0003
+1

你在編譯版本嗎?因爲我聽說在方法頭文件中,調試編譯有NOP代碼。 – 2013-03-22 22:30:07

+0

我正在編譯Debug。 – cm007 2013-03-22 22:30:40

回答

9

C#編譯器在大括號處發出NOP指令。這使得它更容易在您的代碼中設置斷點lot。調試器只允許在代碼上設置斷點,而大括號通常不會產生任何代碼。所以這只是一個簡單的調試幫助,這些NOP不會在發佈版本中生成。

BR.S指令是編譯器中的一個小缺陷,它沒有peephole optimizer來擺脫這些多餘的指令。一般來說,C#編譯器的工作不是優化代碼,這是由the jitter完成的。這將輕鬆而輕鬆地刪除指令。

+0

我很確定分支將在發佈模式下被刪除。通常情況下,這種垃圾是由早期的編譯器產生的,並且刪除它是微不足道的。 – usr 2013-03-22 22:50:46

+0

我的問題更多的是,爲什麼'L_000e'上有'nop'操作碼,而不是'L_0006'和'L_0007'之間?我發現在'L_0000'處需要一個'nop'操作碼來在開始該方法的花括號處設置一個斷點,但爲什麼在'L_000e'而不是'L_0006'和'L_0007'之間? – cm007 2013-03-23 02:00:58

+1

L_0006沒有花括號。 L_000e與BR.S是不必要的相同。這並不重要,這並不是完美的。錯誤在需要修復時得到修復,而不是因爲它們存在。從遞歸體面分析器中擺脫僞代碼可能很困難,修復它並不總是值得冒犯它。如果您想追查原因,那麼您可以從SSCLI20發行版csharp/sccomp子目錄研究C#編譯器的源代碼。 – 2013-03-23 02:21:52

1

所有你看到的是因爲你在調試模式下編譯。冗餘跳轉和nops是禁用的優化傳遞以及調試支持(我相信)。

編譯爲發行模式。

+0

謝謝。在釋放模式下編譯時,MethodB只是兩條指令'ldarg.1'' ret'。 – cm007 2013-03-23 01:56:57

相關問題