【小白學C#】淺談.NET中的IL代碼

1、前言

  前幾天羣裏有位水友提問:」C#中,當一個方法所傳入的參數是一個靜態字段的時候,程序是直接到靜態字段拿數據仍是從複製的函數棧中拿數據「。其實很明顯,這和方法參數的傳遞方式有關,若是是引用傳遞的話,確定是會去靜態字段直接拿值的;若是方法是以傳值的方式使用參數的話,必定是從複製的棧中拿值的。html

  可是這位水友就是不相信這個結論(後來發現,這貨整一個槓精啊!每天在羣裏跟人擡槓~),這個時候我突然想到了能夠反向看一下C#的IL代碼,來了解一下程序運行的過程。的確,當咱們對運行結果有異議的時候,能夠經過IL代碼透過表面看本質。數組

2、IL簡介

  在咱們分析查看IL以前首先要了解下什麼是IL?IL的全稱是Intermediate Language (IL)即將.NET代碼轉化爲機器語言的一箇中間語言的縮寫。在必定程度上,咱們能夠將其理解爲僞彙編語言。咱們在使用.NET框架中的C#、VB.NET、F#等語言的時候,編譯過程並非像C/C++同樣直接編譯出原生代碼,而是編譯成IL中間語言。經過IL中間語言這種方式,能夠實現跨平臺、提升程序靈活性等多種優勢。緩存

  下面咱們以C#語言爲例,大體瞭解瞭解一下咱們的源代碼是如何編譯成IL語言,繼而運行在電腦上面的。性能優化

  

  圖1:.NET語言編譯過程示意圖框架

  上圖爲C#語言的編譯運行過程示意圖。首先編譯器將咱們編寫好的源代碼編譯成IL中間語言,這些IL中間語言的主要內容是一些元數據和中間語言指令。而後再由咱們的JIT編譯器加載這些IL中間語言,JIT編譯器會根據系統環境將IL中間語言指令轉換爲機器碼,繼而運行在不一樣的目標平臺上,實現跨平臺功能。(JIT編譯器將IL中間語言即時編譯成原生語言的過程和解釋性語言的讀取一條執行一條又有些不一樣,JIT會對編譯結果進行緩存以便下次調取的時候直接使用)這也是爲何有些ASP.NET網站第一次運行時會較慢,然後面的執行速度則會相對快不少的一個緣由。函數

  再總結一下上面所說的編譯過程:工具

    • 首先,編譯器要編歷源代碼,經過大量的計算生成IL中間代碼,這些代碼並不能直接地被CPU使用,還須要第二步操做;
    • 接下來,運行時將這些IL代碼經過JIT編譯器進一步編譯成原生的CPU指令。

  在上文中咱們提到了一個JIT編譯器,它的全名叫即時編譯器。顧名思義,它是在運行時環境中發生的編譯行爲。讀到這裏,相信不少朋友可能都會像馬三同樣產生疑問了。相比傳統的直接將源代碼編譯成原生代碼,C#將源代碼編譯成了中間語言不會下降效率嘛?原來直接一步到位的過程,如今偏要拆成兩個部分。這不只要花費更多的時間、佔用更多的內存,還有可能下降性能,那用JIT編譯器的好處到底有什麼呢?性能

  其實,使用JIT編譯器的好處多多。若是咱們不用JIT即時編譯將編譯後的程序放在運行時中的話,那就只能生成在在某一種CPU平臺上運行的原生代碼,若是程序要運行在多種目標平臺上的話,就要編譯多種目標平臺的原生代碼,這樣C#也就失去了跨平臺能力。其次,JIT即時編譯生成原生代碼發生在運行時階段,所以即時編譯器會對生成的原生代碼進行性能優化。通過優化的原生代碼要比不優化的代碼性能好。測試

  在咱們的Unity遊戲開發中就存在着AOT編譯和JIT編譯兩種編譯方式,之後咱們會單獨開篇博客來詳細探討一下這兩種編譯方式的異同,這裏就再也不贅述了。優化

3、如何使用ILDasm工具查看IL代碼

  上面說了一大堆概念和理論,相信你們早已經技癢,別急,下面,馬三就和你們一塊兒使用ILDasm工具反編譯並查看IL代碼。ILDasm工具通常在咱們安裝Visual Studio的時候就已經默認安裝好了,查看IL代碼一般只需下面的幾步操做:

  1.首先,須要打開ILDasm工具,點擊電腦桌面的開始,而後在程序中找到對應版本的Visual Studio的目錄,接着在裏面找到VS開發人員命令提示,英文版的名稱是:Developer Command Prompt 。點擊打開它,而後在命令行裏面輸入ILDasm並回車,就會打開ILDasm工具了。

  

  圖2:VS開發人員命令提示示意圖

   2.而後,在ILDasm工具的界面,點擊文件-->打開,而後選擇咱們預先編譯出來的.exe文件,ILDasm工具就會自動的幫咱們分析出IL代碼及其代碼組織結構,以下圖所示:

  

  圖3:解析出來的IL代碼及相關代碼組織結構

   解析出來的IL代碼,會以一些小圖標區分標識出函數、接口等不一樣的部分,ILDasm中圖標含義以下圖所示:

  

  圖4:ILDasm中圖標含義

   經過上面的兩步,咱們就能夠輕鬆的反編譯出程序的IL代碼,下面咱們經過一個小案例,來解讀一下IL代碼的運行流程以及分析IL代碼的方式。

4、淺析IL代碼

  好了,如今讓咱們回到博客最初拋出的那個問題上面來:「C#中,當一個方法所傳入的參數是一個靜態字段的時候,程序是直接到靜態字段拿數據仍是從複製的函數棧中拿數據?」下面咱們就以這個問題爲例子,經過反編譯出IL代碼,簡單地驗證一下。

  首先貼出咱們的C#代碼,很簡單,在兩個方法中分別以傳值和傳遞引用的方式傳入同一個靜態變量:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace ILDemo
 8 {
 9 
10     class Program
11     {
12         public static int n = 0;
13         static void Main(string[] args)
14         {
15             Func1(n);
16             Console.WriteLine(n);
17             Func2(ref n);
18             Console.WriteLine(n);
19         }
20 
21         public static void Func1(int n)
22         {
23             n += 5;
24         }
25 
26         public static void Func2(ref int n)
27         {
28             n += 5;
29         }
30     }
31 
32 }

  而後咱們對上面的代碼進行編譯,以後再按照第三節的操做,將其反編譯成IL代碼,如下是反編譯出來的IL代碼結構:

  

  圖5:反編譯IL代碼結構

  首先讓咱們觀察一下Func1反編譯出來的IL代碼,以下圖所示:

  

  圖6:Func1反編譯出來的IL代碼

  馬三簡單地給你們分析一下上面的IL代碼的意思:

  nop:沒有什麼意義;

  ldarg.0:將索引爲 0 的參數加載到計算堆棧上,也就是把參數 n 放到堆棧上;

  ldc.i4.5:將整數值 5 做爲 int32 推送到計算堆棧上,由於咱們在程序中執行了+5的操做(上面的C#代碼),因此須要先把5推送到堆棧上,以便下一步進行add操做;

  add:將兩個值相加並將結果推送到計算堆棧上,這裏是將 n和5 相加,而後把結果推送到堆棧上;

  Starg.S n:將位於計算堆棧頂部的值存儲在參數槽中的指定索引處,即將n進行存儲

  ret:從當前方法返回,並將返回值(若是存在)從調用方的計算堆棧推送到被調用方的計算堆棧上。

  接着,咱們再來看看Func2反編譯出來的IL代碼:

  

  圖7:Func2反編譯出來的IL代碼

  能夠看到,由於咱們的C#代碼中使用了ref參數,因此在IL代碼中將其翻譯成了int32& n的形式,和C++是否是很相似?

  nop:沒有什麼意義;

  ldarg.0:將索引爲 0 的參數加載到計算堆棧上,也就是把參數 n的地址放到堆棧上;

  ldind.i4:將 int32 類型的值做爲 int32 間接加載到計算堆棧上

  ldc.i4.5:將整數值 5 做爲 int32 推送到計算堆棧上,由於咱們在程序中執行了+5的操做(上面的C#代碼),因此須要先把5推送到堆棧上,以便下一步進行add操做;

  add:將兩個值相加並將結果推送到計算堆棧上,這裏是將 n和5 相加,而後把結果推送到堆棧上;

  stind.i4:在所提供的地址存儲 int32 類型的值,即把咱們計算出來的結果,再存儲回靜態變量n的地址裏面;

  ret:從當前方法返回,並將返回值(若是存在)從調用方的計算堆棧推送到被調用方的計算堆棧上。

  經過比較,咱們能夠很明顯地發現因爲參數傳遞的方式不一樣,Func1和Func2調用的IL指令也不近相同,最後讓咱們再看一下Main函數的執行過程再作分析。

  

  圖8:Main函數反編譯出來的IL代碼

  反編譯出來的Main函數IL指令執行流程以下:

  nop:沒有什麼意義;

  Ldsfld:將靜態字段的值推送到計算堆棧上,這裏就是把n的值推送到計算堆棧上;

  call:調用由傳遞的方法說明符指示的方法,也就是調用了Func1函數;

  nop:沒有什麼意義;

  Ldsfld:將靜態字段的值推送到計算堆棧上,這裏就是把n的值推送到計算堆棧上;

  call:調用由傳遞的方法說明符指示的方法,調用打印方法,將n的值輸出到控制檯

  nop:沒有什麼意義;

  Ldsflda:將靜態字段的地址推送到計算堆棧上,就是把靜態變量n在內存中的地址推送到計算堆棧上;

  call:調用由傳遞的方法說明符指示的方法,也就是調用了Func2函數;

  nop:沒有什麼意義;

  Ldsfld:將靜態字段的值推送到計算堆棧上,這裏就是把n的值推送到計算堆棧上;

  call:調用由傳遞的方法說明符指示的方法,調用打印方法,將n的值輸出到控制檯

  通過上面的一系列漫長地分析,咱們能夠得出結論:C#中,當一個方法所傳入的參數是一個靜態字段的時候,若是是引用傳遞的話,確定是會去靜態字段直接拿值的;若是方法是以傳值的方式使用參數的話,必定是從複製的棧中拿值的。「」

5、IL代碼指令對照表

  爲了方便你們對照查看IL代碼,理解IL指令的意義,馬三在這裏給你們提供了一個詳細的IL指令對照表(Zery提供),原版的IL指令對照表你們能夠到MSDN上面查看,附上連接

名稱 說明
Add 將兩個值相加並將結果推送到計算堆棧上。
Add.Ovf 將兩個整數相加,執行溢出檢查,而且將結果推送到計算堆棧上。
Add.Ovf.Un 將兩個無符號整數值相加,執行溢出檢查,而且將結果推送到計算堆棧上。
And 計算兩個值的按位「與」並將結果推送到計算堆棧上。
Arglist 返回指向當前方法的參數列表的非託管指針。
Beq 若是兩個值相等,則將控制轉移到目標指令。
Beq.S 若是兩個值相等,則將控制轉移到目標指令(短格式)。
Bge 若是第一個值大於或等於第二個值,則將控制轉移到目標指令。
Bge.S 若是第一個值大於或等於第二個值,則將控制轉移到目標指令(短格式)。
Bge.Un 當比較無符號整數值或不可排序的浮點型值時,若是第一個值大於第二個值,則將控制轉移到目標指令。
Bge.Un.S 當比較無符號整數值或不可排序的浮點型值時,若是第一個值大於第二個值,則將控制轉移到目標指令(短格式)。
Bgt 若是第一個值大於第二個值,則將控制轉移到目標指令。
Bgt.S 若是第一個值大於第二個值,則將控制轉移到目標指令(短格式)。
Bgt.Un 當比較無符號整數值或不可排序的浮點型值時,若是第一個值大於第二個值,則將控制轉移到目標指令。
Bgt.Un.S 當比較無符號整數值或不可排序的浮點型值時,若是第一個值大於第二個值,則將控制轉移到目標指令(短格式)。
Ble 若是第一個值小於或等於第二個值,則將控制轉移到目標指令。
Ble.S 若是第一個值小於或等於第二個值,則將控制轉移到目標指令(短格式)。
Ble.Un 當比較無符號整數值或不可排序的浮點型值時,若是第一個值小於或等於第二個值,則將控制轉移到目標指令。
Ble.Un.S 當比較無符號整數值或不可排序的浮點值時,若是第一個值小於或等於第二個值,則將控制權轉移到目標指令(短格式)。
Blt 若是第一個值小於第二個值,則將控制轉移到目標指令。
Blt.S 若是第一個值小於第二個值,則將控制轉移到目標指令(短格式)。
Blt.Un 當比較無符號整數值或不可排序的浮點型值時,若是第一個值小於第二個值,則將控制轉移到目標指令。
Blt.Un.S 當比較無符號整數值或不可排序的浮點型值時,若是第一個值小於第二個值,則將控制轉移到目標指令(短格式)。
Bne.Un 當兩個無符號整數值或不可排序的浮點型值不相等時,將控制轉移到目標指令。
Bne.Un.S 當兩個無符號整數值或不可排序的浮點型值不相等時,將控制轉移到目標指令(短格式)。
Box 將值類轉換爲對象引用(O 類型)。
Br 無條件地將控制轉移到目標指令。
Br.S 無條件地將控制轉移到目標指令(短格式)。
Break 向公共語言結構 (CLI) 發出信號以通知調試器已撞上了一個斷點。
Brfalse 若是 value 爲 false、空引用(Visual Basic 中的 Nothing)或零,則將控制轉移到目標指令。
Brfalse.S 若是 value 爲 false、空引用或零,則將控制轉移到目標指令。
Brtrue 若是 value 爲 true、非空或非零,則將控制轉移到目標指令。
Brtrue.S 若是 value 爲 true、非空或非零,則將控制轉移到目標指令(短格式)。
Call 調用由傳遞的方法說明符指示的方法。
Calli 經過調用約定描述的參數調用在計算堆棧上指示的方法(做爲指向入口點的指針)。
Callvirt 對對象調用後期綁定方法,而且將返回值推送到計算堆棧上。
Castclass 嘗試將引用傳遞的對象轉換爲指定的類。
Ceq 比較兩個值。若是這兩個值相等,則將整數值 1 (int32) 推送到計算堆棧上;不然,將 0 (int32) 推送到計算堆棧上。
Cgt 比較兩個值。若是第一個值大於第二個值,則將整數值 1 (int32) 推送到計算堆棧上;反之,將 0 (int32) 推送到計算堆棧上。
Cgt.Un 比較兩個無符號的或不可排序的值。若是第一個值大於第二個值,則將整數值 1 (int32) 推送到計算堆棧上;反之,將 0 (int32) 推送到計算堆棧上。
Ckfinite 若是值不是有限數,則引起 ArithmeticException。
Clt 比較兩個值。若是第一個值小於第二個值,則將整數值 1 (int32) 推送到計算堆棧上;反之,將 0 (int32) 推送到計算堆棧上。
Clt.Un 比較無符號的或不可排序的值 value1 和 value2。若是 value1 小於 value2,則將整數值 1 (int32 ) 推送到計算堆棧上;反之,將 0 ( int32 ) 推送到計算堆棧上。
Constrained 約束要對其進行虛方法調用的類型。
Conv.I 將位於計算堆棧頂部的值轉換爲 native int。
Conv.I1 將位於計算堆棧頂部的值轉換爲 int8,而後將其擴展(填充)爲 int32。
Conv.I2 將位於計算堆棧頂部的值轉換爲 int16,而後將其擴展(填充)爲 int32。
Conv.I4 將位於計算堆棧頂部的值轉換爲 int32。
Conv.I8 將位於計算堆棧頂部的值轉換爲 int64。
Conv.Ovf.I 將位於計算堆棧頂部的有符號值轉換爲有符號 native int,並在溢出時引起 OverflowException。
Conv.Ovf.I.Un 將位於計算堆棧頂部的無符號值轉換爲有符號 native int,並在溢出時引起 OverflowException。
Conv.Ovf.I1 將位於計算堆棧頂部的有符號值轉換爲有符號 int8 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.I1.Un 將位於計算堆棧頂部的無符號值轉換爲有符號 int8 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.I2 將位於計算堆棧頂部的有符號值轉換爲有符號 int16 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.I2.Un 將位於計算堆棧頂部的無符號值轉換爲有符號 int16 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.I4 將位於計算堆棧頂部的有符號值轉換爲有符號 int32,並在溢出時引起 OverflowException。
Conv.Ovf.I4.Un 將位於計算堆棧頂部的無符號值轉換爲有符號 int32,並在溢出時引起 OverflowException。
Conv.Ovf.I8 將位於計算堆棧頂部的有符號值轉換爲有符號 int64,並在溢出時引起 OverflowException。
Conv.Ovf.I8.Un 將位於計算堆棧頂部的無符號值轉換爲有符號 int64,並在溢出時引起 OverflowException。
Conv.Ovf.U 將位於計算堆棧頂部的有符號值轉換爲 unsigned native int,並在溢出時引起 OverflowException。
Conv.Ovf.U.Un 將位於計算堆棧頂部的無符號值轉換爲 unsigned native int,並在溢出時引起 OverflowException。
Conv.Ovf.U1 將位於計算堆棧頂部的有符號值轉換爲 unsigned int8 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.U1.Un 將位於計算堆棧頂部的無符號值轉換爲 unsigned int8 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.U2 將位於計算堆棧頂部的有符號值轉換爲 unsigned int16 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.U2.Un 將位於計算堆棧頂部的無符號值轉換爲 unsigned int16 並將其擴展爲 int32,並在溢出時引起 OverflowException。
Conv.Ovf.U4 將位於計算堆棧頂部的有符號值轉換爲 unsigned int32,並在溢出時引起 OverflowException。
Conv.Ovf.U4.Un 將位於計算堆棧頂部的無符號值轉換爲 unsigned int32,並在溢出時引起 OverflowException。
Conv.Ovf.U8 將位於計算堆棧頂部的有符號值轉換爲 unsigned int64,並在溢出時引起 OverflowException。
Conv.Ovf.U8.Un 將位於計算堆棧頂部的無符號值轉換爲 unsigned int64,並在溢出時引起 OverflowException。
Conv.R.Un 將位於計算堆棧頂部的無符號整數值轉換爲 float32。
Conv.R4 將位於計算堆棧頂部的值轉換爲 float32。
Conv.R8 將位於計算堆棧頂部的值轉換爲 float64。
Conv.U 將位於計算堆棧頂部的值轉換爲 unsigned native int,而後將其擴展爲 native int。
Conv.U1 將位於計算堆棧頂部的值轉換爲 unsigned int8,而後將其擴展爲 int32。
Conv.U2 將位於計算堆棧頂部的值轉換爲 unsigned int16,而後將其擴展爲 int32。
Conv.U4 將位於計算堆棧頂部的值轉換爲 unsigned int32,而後將其擴展爲 int32。
Conv.U8 將位於計算堆棧頂部的值轉換爲 unsigned int64,而後將其擴展爲 int64。
Cpblk 將指定數目的字節從源地址複製到目標地址。
Cpobj 將位於對象(&、* 或 native int 類型)地址的值類型複製到目標對象(&、* 或 native int 類型)的地址。
Div 將兩個值相除並將結果做爲浮點(F 類型)或商(int32 類型)推送到計算堆棧上。
Div.Un 兩個無符號整數值相除並將結果 ( int32 ) 推送到計算堆棧上。
Dup 複製計算堆棧上當前最頂端的值,而後將副本推送到計算堆棧上。
Endfilter 將控制從異常的 filter 子句轉移回公共語言結構 (CLI) 異常處理程序。
Endfinally 將控制從異常塊的 fault 或 finally 子句轉移回公共語言結構 (CLI) 異常處理程序。
Initblk 將位於特定地址的內存的指定塊初始化爲給定大小和初始值。
Initobj 將位於指定地址的值類型的每一個字段初始化爲空引用或適當的基元類型的 0。
Isinst 測試對象引用(O 類型)是否爲特定類的實例。
Jmp 退出當前方法並跳至指定方法。
Ldarg 將參數(由指定索引值引用)加載到堆棧上。
Ldarg.0 將索引爲 0 的參數加載到計算堆棧上。
Ldarg.1 將索引爲 1 的參數加載到計算堆棧上。
Ldarg.2 將索引爲 2 的參數加載到計算堆棧上。
Ldarg.3 將索引爲 3 的參數加載到計算堆棧上。
Ldarg.S 將參數(由指定的短格式索引引用)加載到計算堆棧上。
Ldarga 將參數地址加載到計算堆棧上。
Ldarga.S 以短格式將參數地址加載到計算堆棧上。
Ldc.I4 將所提供的 int32 類型的值做爲 int32 推送到計算堆棧上。
Ldc.I4.0 將整數值 0 做爲 int32 推送到計算堆棧上。
Ldc.I4.1 將整數值 1 做爲 int32 推送到計算堆棧上。
Ldc.I4.2 將整數值 2 做爲 int32 推送到計算堆棧上。
Ldc.I4.3 將整數值 3 做爲 int32 推送到計算堆棧上。
Ldc.I4.4 將整數值 4 做爲 int32 推送到計算堆棧上。
Ldc.I4.5 將整數值 5 做爲 int32 推送到計算堆棧上。
Ldc.I4.6 將整數值 6 做爲 int32 推送到計算堆棧上。
Ldc.I4.7 將整數值 7 做爲 int32 推送到計算堆棧上。
Ldc.I4.8 將整數值 8 做爲 int32 推送到計算堆棧上。
Ldc.I4.M1 將整數值 -1 做爲 int32 推送到計算堆棧上。
Ldc.I4.S 將提供的 int8 值做爲 int32 推送到計算堆棧上(短格式)。
Ldc.I8 將所提供的 int64 類型的值做爲 int64 推送到計算堆棧上。
Ldc.R4 將所提供的 float32 類型的值做爲 F (float) 類型推送到計算堆棧上。
Ldc.R8 將所提供的 float64 類型的值做爲 F (float) 類型推送到計算堆棧上。
Ldelem 按照指令中指定的類型,將指定數組索引中的元素加載到計算堆棧的頂部。
Ldelem.I 將位於指定數組索引處的 native int 類型的元素做爲 native int 加載到計算堆棧的頂部。
Ldelem.I1 將位於指定數組索引處的 int8 類型的元素做爲 int32 加載到計算堆棧的頂部。
Ldelem.I2 將位於指定數組索引處的 int16 類型的元素做爲 int32 加載到計算堆棧的頂部。
Ldelem.I4 將位於指定數組索引處的 int32 類型的元素做爲 int32 加載到計算堆棧的頂部。
Ldelem.I8 將位於指定數組索引處的 int64 類型的元素做爲 int64 加載到計算堆棧的頂部。
Ldelem.R4 將位於指定數組索引處的 float32 類型的元素做爲 F 類型(浮點型)加載到計算堆棧的頂部。
Ldelem.R8 將位於指定數組索引處的 float64 類型的元素做爲 F 類型(浮點型)加載到計算堆棧的頂部。
Ldelem.Ref 將位於指定數組索引處的包含對象引用的元素做爲 O 類型(對象引用)加載到計算堆棧的頂部。
Ldelem.U1 將位於指定數組索引處的 unsigned int8 類型的元素做爲 int32 加載到計算堆棧的頂部。
Ldelem.U2 將位於指定數組索引處的 unsigned int16 類型的元素做爲 int32 加載到計算堆棧的頂部。
Ldelem.U4 將位於指定數組索引處的 unsigned int32 類型的元素做爲 int32 加載到計算堆棧的頂部。
Ldelema 將位於指定數組索引的數組元素的地址做爲 & 類型(託管指針)加載到計算堆棧的頂部。
Ldfld 查找對象中其引用當前位於計算堆棧的字段的值。
Ldflda 查找對象中其引用當前位於計算堆棧的字段的地址。
Ldftn 將指向實現特定方法的本機代碼的非託管指針(native int 類型)推送到計算堆棧上。
Ldind.I 將 native int 類型的值做爲 native int 間接加載到計算堆棧上。
Ldind.I1 將 int8 類型的值做爲 int32 間接加載到計算堆棧上。
Ldind.I2 將 int16 類型的值做爲 int32 間接加載到計算堆棧上。
Ldind.I4 將 int32 類型的值做爲 int32 間接加載到計算堆棧上。
Ldind.I8 將 int64 類型的值做爲 int64 間接加載到計算堆棧上。
Ldind.R4 將 float32 類型的值做爲 F (float) 類型間接加載到計算堆棧上。
Ldind.R8 將 float64 類型的值做爲 F (float) 類型間接加載到計算堆棧上。
Ldind.Ref 將對象引用做爲 O(對象引用)類型間接加載到計算堆棧上。
Ldind.U1 將 unsigned int8 類型的值做爲 int32 間接加載到計算堆棧上。
Ldind.U2 將 unsigned int16 類型的值做爲 int32 間接加載到計算堆棧上。
Ldind.U4 將 unsigned int32 類型的值做爲 int32 間接加載到計算堆棧上。
Ldlen 將從零開始的、一維數組的元素的數目推送到計算堆棧上。
Ldloc 將指定索引處的局部變量加載到計算堆棧上。
Ldloc.0 將索引 0 處的局部變量加載到計算堆棧上。
Ldloc.1 將索引 1 處的局部變量加載到計算堆棧上。
Ldloc.2 將索引 2 處的局部變量加載到計算堆棧上。
Ldloc.3 將索引 3 處的局部變量加載到計算堆棧上。
Ldloc.S 將特定索引處的局部變量加載到計算堆棧上(短格式)。
Ldloca 將位於特定索引處的局部變量的地址加載到計算堆棧上。
Ldloca.S 將位於特定索引處的局部變量的地址加載到計算堆棧上(短格式)。
Ldnull 將空引用(O 類型)推送到計算堆棧上。
Ldobj 將地址指向的值類型對象複製到計算堆棧的頂部。
Ldsfld 將靜態字段的值推送到計算堆棧上。
Ldsflda 將靜態字段的地址推送到計算堆棧上。
Ldstr 推送對元數據中存儲的字符串的新對象引用。
Ldtoken 將元數據標記轉換爲其運行時表示形式,並將其推送到計算堆棧上。
Ldvirtftn 將指向實現與指定對象關聯的特定虛方法的本機代碼的非託管指針(native int 類型)推送到計算堆棧上。
Leave 退出受保護的代碼區域,無條件將控制轉移到特定目標指令。
Leave.S 退出受保護的代碼區域,無條件將控制轉移到目標指令(縮寫形式)。
Localloc 從本地動態內存池分配特定數目的字節並將第一個分配的字節的地址(瞬態指針,* 類型)推送到計算堆棧上。
Mkrefany 將對特定類型實例的類型化引用推送到計算堆棧上。
Mul 將兩個值相乘並將結果推送到計算堆棧上。
Mul.Ovf 將兩個整數值相乘,執行溢出檢查,並將結果推送到計算堆棧上。
Mul.Ovf.Un 將兩個無符號整數值相乘,執行溢出檢查,並將結果推送到計算堆棧上。
Neg 對一個值執行求反並將結果推送到計算堆棧上。
Newarr 將對新的從零開始的一維數組(其元素屬於特定類型)的對象引用推送到計算堆棧上。
Newobj 建立一個值類型的新對象或新實例,並將對象引用(O 類型)推送到計算堆棧上。
Nop 若是修補操做碼,則填充空間。儘管可能消耗處理週期,但未執行任何有意義的操做。
Not 計算堆棧頂部整數值的按位求補並將結果做爲相同的類型推送到計算堆棧上。
Or 計算位於堆棧頂部的兩個整數值的按位求補並將結果推送到計算堆棧上。
Pop 移除當前位於計算堆棧頂部的值。
Prefix1 基礎結構。此指令爲保留指令。
Prefix2 基礎結構。此指令爲保留指令。
Prefix3 基礎結構。此指令爲保留指令。
Prefix4 基礎結構。此指令爲保留指令。
Prefix5 基礎結構。此指令爲保留指令。
Prefix6 基礎結構。此指令爲保留指令。
Prefix7 基礎結構。此指令爲保留指令。
Prefixref 基礎結構。此指令爲保留指令。
Readonly 指定後面的數組地址操做在運行時不執行類型檢查,而且返回可變性受限的託管指針。
Refanytype 檢索嵌入在類型化引用內的類型標記。
Refanyval 檢索嵌入在類型化引用內的地址(& 類型)。
Rem 將兩個值相除並將餘數推送到計算堆棧上。
Rem.Un 將兩個無符號值相除並將餘數推送到計算堆棧上。
Ret 從當前方法返回,並將返回值(若是存在)從調用方的計算堆棧推送到被調用方的計算堆棧上。
Rethrow 再次引起當前異常。
Shl 將整數值左移(用零填充)指定的位數,並將結果推送到計算堆棧上。
Shr 將整數值右移(保留符號)指定的位數,並將結果推送到計算堆棧上。
Shr.Un 將無符號整數值右移(用零填充)指定的位數,並將結果推送到計算堆棧上。
Sizeof 將提供的值類型的大小(以字節爲單位)推送到計算堆棧上。
Starg 將位於計算堆棧頂部的值存儲到位於指定索引的參數槽中。
Starg.S 將位於計算堆棧頂部的值存儲在參數槽中的指定索引處(短格式)。
Stelem 用計算堆棧中的值替換給定索引處的數組元素,其類型在指令中指定。
Stelem.I 用計算堆棧上的 native int 值替換給定索引處的數組元素。
Stelem.I1 用計算堆棧上的 int8 值替換給定索引處的數組元素。
Stelem.I2 用計算堆棧上的 int16 值替換給定索引處的數組元素。
Stelem.I4 用計算堆棧上的 int32 值替換給定索引處的數組元素。
Stelem.I8 用計算堆棧上的 int64 值替換給定索引處的數組元素。
Stelem.R4 用計算堆棧上的 float32 值替換給定索引處的數組元素。
Stelem.R8 用計算堆棧上的 float64 值替換給定索引處的數組元素。
Stelem.Ref 用計算堆棧上的對象 ref 值(O 類型)替換給定索引處的數組元素。
Stfld 用新值替換在對象引用或指針的字段中存儲的值。
Stind.I 在所提供的地址存儲 native int 類型的值。
Stind.I1 在所提供的地址存儲 int8 類型的值。
Stind.I2 在所提供的地址存儲 int16 類型的值。
Stind.I4 在所提供的地址存儲 int32 類型的值。
Stind.I8 在所提供的地址存儲 int64 類型的值。
Stind.R4 在所提供的地址存儲 float32 類型的值。
Stind.R8 在所提供的地址存儲 float64 類型的值。
Stind.Ref 存儲所提供地址處的對象引用值。
Stloc 從計算堆棧的頂部彈出當前值並將其存儲到指定索引處的局部變量列表中。
Stloc.0 從計算堆棧的頂部彈出當前值並將其存儲到索引 0 處的局部變量列表中。
Stloc.1 從計算堆棧的頂部彈出當前值並將其存儲到索引 1 處的局部變量列表中。
Stloc.2 從計算堆棧的頂部彈出當前值並將其存儲到索引 2 處的局部變量列表中。
Stloc.3 從計算堆棧的頂部彈出當前值並將其存儲到索引 3 處的局部變量列表中。
Stloc.S 從計算堆棧的頂部彈出當前值並將其存儲在局部變量列表中的 index 處(短格式)。
Stobj 將指定類型的值從計算堆棧複製到所提供的內存地址中。
Stsfld 用來自計算堆棧的值替換靜態字段的值。
Sub 從其餘值中減去一個值並將結果推送到計算堆棧上。
Sub.Ovf 從另外一值中減去一個整數值,執行溢出檢查,而且將結果推送到計算堆棧上。
Sub.Ovf.Un 從另外一值中減去一個無符號整數值,執行溢出檢查,而且將結果推送到計算堆棧上。
Switch 實現跳轉表。
Tailcall 執行後綴的方法調用指令,以便在執行實際調用指令前移除當前方法的堆棧幀。
Throw 引起當前位於計算堆棧上的異常對象。
Unaligned 指示當前位於計算堆棧上的地址可能沒有與緊接的 ldind、stind、ldfld、stfld、ldobj、stobj、initblk 或 cpblk 指令的天然大小對齊。
Unbox 將值類型的已裝箱的表示形式轉換爲其未裝箱的形式。
Unbox.Any 將指令中指定類型的已裝箱的表示形式轉換成未裝箱形式。
Volatile 指定當前位於計算堆棧頂部的地址能夠是易失的,而且讀取該位置的結果不能被緩存,或者對該地址的多個存儲區不能被取消。
Xor 計算位於計算堆棧頂部的兩個值的按位異或,而且將結果推送到計算堆棧上。

 

引用資料:

http://www.cnblogs.com/zery/p/3368460.html
https://msdn.microsoft.com/en-us/library/812xyxy2

 

做者:馬三小夥兒
出處:http://www.cnblogs.com/msxh/p/7819645.html請尊重別人的勞動成果,讓分享成爲一種美德,歡迎轉載。另外,文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!

相關文章
相關標籤/搜索