2014-10-12 96 views
15

爲了教育目的,我學習了一點IL(主要是因爲我很好奇底層的'%'會發生什麼(原來是REM)並開始離題了......)。爲什麼在這種情況下使用'br.s'IL操作碼?

我寫了一個方法,就返回true,打破下來一點,想知道關於「br.s」操作碼:

.method public hidebysig static bool ReturnTrue() cil managed 
{ 
    // Code size  7 (0x7) 
    .maxstack 1 
    .locals init ([0] bool CS$1$0000) 
    IL_0000: nop 
    IL_0001: ldc.i4.1 
    IL_0002: stloc.0 
    IL_0003: br.s  IL_0005 
    IL_0005: ldloc.0 
    IL_0006: ret 
} // End of method Primes::ReturnTrue 

ldc.i4.1後棧和stloc上推1 .0將它放置在第0個局部,br.s基本上(據我所知)在IL_0005行做了'goto'到ldloc.0。

這是爲什麼?爲什麼沒有IL_0004線,所以這可以省略?

+6

沒有IL_0004,因爲'br.s'是一個雙字節指令。我猜你禁用了優化?是的,然後期待奇怪的事情。當你開啓優化時你會得到什麼? – hvd 2014-10-12 08:51:38

+1

然後它只是推動'1'到堆棧並立即返回。 IL_0000:ldc.i4.1 IL_0001:ret – Apeiron 2014-10-12 08:57:21

+0

在「IL_xxxx」中,xxxx是指令從字節開始偏移的偏移量。 br.s是一個單字節指令(與其餘部分一樣),它需要一個單字節操作數,這是要跳轉到的目標指令。 – 2014-10-12 09:13:23

回答

13

該分支用於調試目的,返回值已計算並存儲,現在調試器可以「調用」。這與方法條目中的NOP相同。

至於IL_0004,作爲@hvd狀態,br.s有一個地址,並且不適合在「行」,這裏一個字節(我不知道你是多麼熟悉的解決,但一個指令通常是一個字節,即8位,以及地址或偏移量,通常爲8,16或32位,在這種情況下,我們有一個8位偏移量的操作碼Wikipedia has a good article on CIL-OP-codes)。

此外,假設你的方法有多種收益,並通過例如if -branches,所有的人都跳到結束,IL_0005你的情況,因此需要在函數返回只有一個斷點。

+0

咦?這是字節數,對於'br.s'操作數只有一個字節,並且地址通常不適合一個字節。 'BR。s'需要一個8位的相對偏移量,而不是一個地址。 – hvd 2014-10-12 09:18:55

+0

@hvd你是對的,我一直假定IL地址不是基於字節的,而是基於字長的,但是你是正確的先生;) – flindeberg 2014-10-12 09:24:34

3

這是一個非常常見的recursive-descent parser的工件,就像C#編譯器使用的工件一樣。擺脫這些分支需要peephole optimizer

當編譯器自己優化了一個平凡的操作,其結果可以在編譯時確定時,可能會發生。 C#編譯器沒有窺視孔優化器,因爲它不是必需的,抖動會消除這些不必要的分支。把優化器放在抖動中通常是一個勝利的策略,每個語言編譯器都從中受益。保持編譯器非常簡單,並且在一個(或幾個)地方編寫和維護代碼優化器的成本很高。

這不是抖動優化器結束的地方,您的整個方法將在運行時消失。由於你的方法的返回值在編譯時是已知的,因此任何調用該方法的代碼都將大大優化。看到這種MSIL是強烈的暗示,你的代碼可以很容易地簡化或有一個錯誤:)

0

這不是發生在Visual Studio 2013上,它似乎像dev最終修復它。 它將在VS2013上看起來像這樣。

.method public hidebysig static bool ReturnTrue() cil managed 
{ 
    .maxstack 1 
    ldc.i4.1 
    ret 
} // End of method Primes::ReturnTrue 
+1

嘗試使用'debug'-flag set進行編譯。 – flindeberg 2016-04-20 11:55:11

相關問題