相關閱讀 |
>>> 技術話題—商業文明的嶄新時代 >>> | 簡體 傳統 |
其實小匹夫在U3D的開發中一直對U3D的跨平臺能力很好奇。到底是什么原理使得U3D可以跨平臺呢?后來發現了Mono的作用,并進一步了解到了CIL的存在。所以,作為一個對Unity3D跨平臺能力感興趣的U3D程序猿,小匹夫如何能不關注CIL這個話題呢?那么下面各位看官就拾起語文老師教導我們的作文口訣(Why,What,How),和小匹夫一起走進CIL的世界吧~ 回到本文的題目,U3D或者說Mono的跨平臺是如何做到的? 如果換做小匹夫或者看官你來做,應該怎么實現一套代碼對應多種平臺呢? 其實原理想想也簡單,生活中也有很多可以參考的例子,比如下圖(誰讓小匹夫是做移動端開發的呢,只能物盡其用從自己身邊找例子了T.T): 像這樣一根線,管你是安卓還是ios都能充電。所以從這個意義上,這貨也實現了跨平臺。那么我們能從它身上學到什么呢?對的,那就是從一樣的能源(電)到不同的平臺(ios,安卓)之間需要一個中間層過度轉換一下。 那么來到U3D為何能跨平臺,簡而言之,其實現原理在于使用了叫CIL(Common Intermediate Language通用中間語言,也叫做MSIL微軟中間語言)的一種代碼指令集,CIL可以在任何支持CLI(Common Language Infrastructure,通用語言基礎結構)的環境中運行,就像.NET是微軟對這一標準的實現,Mono則是對CLI的又一實現。由于CIL能運行在所有支持CLI的環境中,例如剛剛提到的.NET運行時以及Mono運行時,也就是說和具體的平臺或者CPU無關。這樣就無需根據平臺的不同而部署不同的內容了。所以到這里,各位也應該恍然大了。代碼的編譯只需要分為兩部分就好了嘛: 上文也說了CIL是指令集,但是不是還是太模糊了呢?所以語文老師教導我們,描述一個東西時肯定要先從外貌寫起。遵循老師的教導,我們不妨先通過工具來看看CIL到底長什么樣。 工具就是ildasm了。下面小匹夫寫一個簡單的.cs看看生成的CIL代碼長什么樣。 C#代碼: CIL代碼: 好啦。代碼雖然簡單,但是也能說明足夠多的問題。那么和CIL的第一次親密接觸,能給我們留下什么直觀的印象呢? 直觀的印象有了,但是離我們的短期目標,說清楚(或者說介紹個大概)CIL是What,甚至是終極目標,搞明白Uniyt3D為何能跨平臺還有2萬4千9百里的距離。 好啦,話不多說,繼續亂侃。 參照附錄中的操作碼表,對照可以總結出一份更易讀的表格。那就是如下的表啦。 在此,小匹夫想請各位認真讀表,然后心中默數3個數,最后看看都能發現些什么。 如果是小匹夫的話,第一感覺就是基本每一條描述中都包含一個棧。不錯,CIL是基于堆棧的,也就是說CIL的VM(mono運行時)是一個棧式機。這就意味著數據是推入堆棧,通過堆棧來操作的,而非通過CPU的寄存器來操作,這更加驗證了其和具體的CPU架構沒有關系。為了說明這一點,小匹夫舉個例子好啦。 大學時候學單片機(大概是8086,記不清了)的時候記得做加法大概是這樣的: 其中的eax是啥?寄存器。所以如果CIL處理數據要通過cpu的寄存器的話,那也就不可能和cpu的架構無關了。 當然,CIL之所以是基于堆棧而非CPU的另一個原因是相比較于cpu的寄存器,操作堆棧實在太簡單了。回到剛才小匹夫說的大學時候曾經學過的單片機那門課程上,當時記得各種寄存器,各種標志位,各種。。。,而堆棧只需要簡單的壓棧和彈出,因此對于虛擬機的實現來說是再合適不過了。所以想要更具體的了解CIL基于堆棧這一點,各位可以去看一下堆棧方面的內容。這里小匹夫就不拓展了。 那么第二感覺呢?貌似附錄的表中有new對象的語句呀。嗯,的確,CIL同樣是面向對象的。 這意味著什么呢?那就是在CIL中你可以創建對象,調用對象的方法,訪問對象的成員。而這里需要注意的就是對方法的調用。 回到上表中的右上角。對,就是對參數的操作部分。靜態方法和實例方法是不同的哦~ 舉個例子:假設你有一個類Murong中有一個靜態方法Add(int32 a, int32 b),實現的內容就如同它的名字一樣使兩個數相加,所以需要2個參數。和一個實例方法TellName(string name),這個方法會告訴你傳入的名字。 那么其中的靜態方法Add的CIL代碼如下: 那么我們調用這個靜態函數應該就是這樣咯。 對應的CIL代碼為: 可見CIL直接call了Murong的Add方法,而不需要一個Murong的實例。 Murong類中的實例方法TellName()的CIL代碼如下: 看到和靜態方法的區別了嗎?對,第一個參數對應的是ldarg.1中的參數1,而不是靜態方法中的0。因為此時參數0相當于this,this是不用參與參數傳遞的。 那么我們再看看調用實例方法的C#代碼和對應的CIL代碼是如何的。 CIL: 到此,受制于篇幅所限(小匹夫不想寫那么多字啊啊啊!)CIL是What的問題大致介紹一下。當然沒有再拓展,以后小匹夫可能會再詳細寫一下這塊。 記得語文老師說過,寫作文最重要的一點是要首尾呼應。既然咱們開篇就提出了U3D為何能跨平臺的問題,那么接近文章的結尾咱們就再來 提問: Q:上面的Why部分,咱們知道了U3D能跨平臺是因為存在著一個能通吃的中間語言CIL,這也是所謂跨平臺的前提,但是為啥CIL能通吃各大平臺呢?當然可以說CIL基于堆棧,跟你CPU怎么架構的沒啥關系,但是感覺過于理論化、學術化,那還有沒有通俗化、工程化的說法呢? A:原因就是前面小匹夫提到過的,.Net運行時和Mono運行時。也就是說CIL語言其實是運行在虛擬機中的,具體到咱們的U3D也就是mono的運行時了,換言之mono運行的其實CIL語言,CIL也并非真正的在本地運行,而是在mono運行時中運行的,運行在本地的是被編譯后生成的原生代碼。當然看官博的文章,他們似乎也在開發自己的mono,也就是被稱為腳本的未來的IL2Cpp,這種類似運行時的功能是將IL再編譯成c++,再由c++編譯成原生代碼,據說效率提升很可觀,小匹夫也是蠻期待的。 這里為了實現跨平臺式的演示,小匹夫用mac給各位做個測試好啦: 新建一個cs文件,然后使用mono來運行。這個cs文件內容如下: 然后咱們直接在命令行中運行這個cs文件試試~ 說的很清楚,文件沒有包含一個CIL映像。可見mono是不能直接運行cs文件的。假如我們把它編譯成CIL呢?那么我們用mono帶的mcs來編譯小匹夫的Test.cs文件。 生成了什么呢?如圖: 好像沒見有叫.IL的文件生成啊?反而好像多了一個.exe文件?可是沒聽說Mac能運行exe文件呀?可為啥又生成了.exe呢?各位看官可能要說,小匹夫你是不是拿windows截圖P的啊?嘿嘿,小匹夫可不敢。辣么真相其實就是這個exe并不是讓Mac來運行的,而是留給mono運行時來運行的,換言之這個文件的可執行代碼形式是CIL的位元碼形態。到此,我們完成了從C#到CIL的過程。接下來就讓我們運行下剛剛的成果好啦。 結果是輸出了一個大大的Hi。這里,就引出了下一個部分。 這個HI可是在小匹夫的MAC終端上出現的呀,那么就證明這個C#寫的代碼在MAC上運行的還挺嗨。 為啥呢?為啥C#寫的代碼能跑在MAC上呢?這就不得不提從CIL如何到本機原生代碼的過程了。Mono提供了兩種編譯方式,就是我們經常能看到的:JIT(Just-in-Time compilation,即時編譯)和AOT(Ahead-of-Time,提前編譯或靜態編譯)。這兩種方式都是將CIL進一步編譯成平臺的原生代碼。這也是實現跨平臺的最后一步。下面就分頭介紹一下。 從名字就能看的出來,即時編譯,或者稱之為動態編譯,是在程序執行時才編譯代碼,解釋一條語句執行一條語句,即將一條中間的托管的語句翻譯成一條機器語句,然后執行這條機器語句。但同時也會將編譯過的代碼進行緩存,而不是每一次都進行編譯。所以可以說它是靜態編譯和解釋器的結合體。不過你想想機器既要處理代碼的邏輯,同時還要進行編譯的工作,所以其運行時的效率肯定是受到影響的。因此,Mono會有一部分代碼通過AOT靜態編譯,以降低在程序運行時JIT動態編譯在效率上的問題。 不過一向嚴苛的IOS平臺是不允許這種動態的編譯方式的,這也是U3D官方無法給出熱更新方案的一個原因。而Android平臺恰恰相反,Dalvik虛擬機使用的就是JIT方案。 其實Mono的AOT靜態編譯和JIT并非對立的。AOT同樣使用了JIT來進行編譯,只不過是被AOT編譯的代碼在程序運行之前就已經編譯好了。當然還有一部分代碼會通過JIT來進行動態編譯。下面小匹夫就手動操作一下mono,讓它進行一次AOT編譯。 結果: 從圖中可以看到JIT time: 39 ms,也就是說Mono的AOT模式其實會使用到JIT,同時我們看到了生成了一個適應小匹夫的MAC的動態庫Test.exe.dylib,而在Linux生成就是.so(共享庫)。 AOT編譯出來的庫,除了包括我們的代碼之外,還有被緩存的元數據信息。所以我們甚至可以只編譯元數據信息而不變異代碼。例如這樣: 可見代碼沒有被包括進來。 那么簡單總結一下AOT的過程: 當然上文也說了,IOS平臺是禁止使用JIT的,可看樣子Mono的AOT模式仍然會保留一部分代碼會在程序運行時動態編譯。所以為了破解這個問題,Mono提供了一個被稱為Full AOT的模式。即預先對程序集中的所有CIL代碼進行AOT編譯生成一個本地代碼映像,然后在運行時直接加載這個映像而不再使用JIT引擎。目前由于技術或實現上的原因在使用Full AOT時有一些限制,不過這里不再多說了。以后也還會更細的分析下AOT。 好啦,寫到現在也已經到了凌晨3:04分了。感覺寫的內容也差不多了。那么對本文的主題U3D為何能跨平臺以及CIL做個最終的總結陳詞: 如果各位看官覺得文章寫得還好,那么就容小匹夫跪求各位給點個推薦,謝啦~ 你也可以在游戲蠻牛讀到這篇文章: 匹夫細說Unity3D(三)為何能跨平臺?聊聊CIL(MSIL)前言:
Why?
What?
class Class1
{public static void Main(string[] args)
{
System.Console.WriteLine("hi");
}
}
.class private auto ansi beforefieldinit Class1
extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint// 代碼大小 13 (0xd).maxstack 8IL_0000: nop
IL_0001: ldstr "hi"IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Class1::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{// 代碼大小 7 (0x7).maxstack 8IL_0000: ldarg.0IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Class1::.ctor} // end of class Class1
主要操作 操作數范圍/條件 操作數類型 操作數 縮寫 全稱 含義 縮寫 全稱 含義 縮寫 全稱 含義 縮寫 全稱 含義 ld load 將操作數壓到堆棧當中,相當于:
push axarg argument 參數 ? ? 操作數中的數值 .0 ? 第零個參數 .1 ? 第一個參數 .2 ? 第二個參數 .3 ? 第三個參數 .s xx (short) 參數xx a address 操作數的地址 只有 .s xx,參見ldarg.s loc local 局部變量 參見ldarg fld field 字段(類的全局變量) 參見ldarg xx ? xx字段,eg:
ldfld xxc const 常量 .i4 int 4 bytes C#里面的int,其他的類型例如short需要通過conv轉換 .m1 minus 1 -1 .0 ? 0 .1 ? 1 .8 8 .s (short) 后面跟一個字節以內的整型數值(有符號的) ? ? 后面跟四個字節的整型數值 .i8 int 8 bytes C#里面的long ? ? 后面跟八個字節的整型數值 .r4 real 4 bytes C#里面的float ? ? 后面跟四個字節的浮點數值 .r8 real 8 bytes C#里面的double ? ? 后面跟八個字節的浮點數值 null null 空值(也就是0) ? ? ? ? ? ? st store 計算堆棧的頂部彈出當前值,相當于:
pop ax參見ld conv convert 數值類型轉換,僅僅用純粹的數值類型間的轉換,例如int/float等 ? ? ? .i1 int 1 bytes C#里面的sbyte ? ? ? .i2 int 2 bytes C#里面的short .i4 int 4 bytes C#里面的int .i8 int 8 bytes C#里面的long .r4 real 4 bytes C#里面的float .r8 real 8 bytes C#里面的double .u4 uint 4 bytes C#里面的uint .u8 uint 8 bytes C#里面的ulong b/br branch 條件和無條件跳轉,相當于:
jmp/jxx label_jumpbr ? ? 無條件跳轉 ? ? ? ? ? 后面跟四個字節的偏移量(有符號) .s (short) 后面跟一個字節的偏移量(有符號) false false 值為零的時候跳轉 ? ? ? 參見br true true 值不為零的時候跳轉 ? ? ? b eq equal to 相等 ? ? ? ne not equal to 不相等 un unsigned or unordered 無氟好的(對于整數)或者無序的(對于浮點) gt greater than 大于 lt less than 小于 ge greater than or equal to 大于等于 le less than or equal to 小于等于 call call 調用 ? ? ? ? ? (非虛函數) ? ? ? ? virt virtual 虛函數 基于堆棧
add eax,-2
面向對象
class Murong
{public void TellName(string name)
{
System.Console.WriteLine(name);
}public static int Add(int a, int b)
{ return a + b;
}
}
靜態方法的處理:
//小匹夫注釋一下。.method public hidebysig static int32 Add(int32 a,
int32 b) cil managed
{ // 代碼大小 9 (0x9)
.maxstack 2
.locals init ([0] int32 CS$1$0000) //初始化局部變量列表。因為我們只返回了一個int型。所以這里聲明了一個int32類型。索引為0
IL_0000: nop
IL_0001: ldarg.0 //將索引為 0 的參數加載到計算堆棧上。
IL_0002: ldarg.1 //將索引為 1 的參數加載到計算堆棧上。
IL_0003: add //計算
IL_0004: stloc.0 //從計算堆棧的頂部彈出當前值并將其存儲到索引 0 處的局部變量列表中。 IL_0005: br.s IL_0007
IL_0007: ldloc.0 //將索引 0 處的局部變量加載到計算堆棧上。
IL_0008: ret //返回該值} // end of method Murong::Add
Murong.Add(1, 2);
IL_0001: ldc.i4.1 //將整數1壓入棧中
IL_0002: ldc.i4.2 //將整數2壓入棧中
IL_0003: call int32 Murong::Add(int32,
int32) //調用靜態方法
實例方法的處理:
.method public hidebysig instance void TellName(string name) cil managed
{ // 代碼大小 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1 //看到和靜態方法的區別了嗎?
IL_0002: call void [mscorlib]System.Console::WriteLine(string)
IL_0007: nop
IL_0008: ret
} // end of method Murong::TellName
//C#
Murong murong = new Murong();
murong.TellName("chenjiadong");.locals init ([0] class Murong murong) //因為C#代碼中定義了一個Murong類型的變量,所以局部變量列表的索引0為該類型的引用。//....IL_0009: newobj instance void Murong::.ctor() //相比上面的靜態方法的調用,此處new一個新對象,出現了instance方法。IL_000e: stloc.0IL_000f: ldloc.0IL_0010: ldstr "chenjiadong" //小匹夫的名字入棧IL_0015: callvirt instance void Murong::TellName(string) //實例方法的調用也有instance
How?
從C#到CIL
mcs Test.cs
mono Test.exe
從CIL到Native Code
JIT即時編譯:
AOT靜態編譯:
//在命令行輸入mono --aot Test.exe
//只包含元數據的信息mono --aot=metadata-only Test.exe
Full AOT
總結:
裝模作樣的聲明一下:本博文章若非特殊注明皆為原創,若需轉載請保留原文鏈接(http://www.cnblogs.com/murongxiaopifu/p/4211964.html)及作者信息慕容小匹夫
(出處: -u3d游戲開發者社區【游戲蠻牛】unity3d官網)附錄:
名稱 說明 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 計算位于計算堆棧頂部的兩個值的按位異或,并且將結果推送到計算堆棧上。
Cnblogs 慕容小匹夫 2015-08-23 08:57:28
稱謂:
内容: