.NET Emit 入門教程:第六部分:IL 指令:9:詳解 ILGenerator 指令方法:運算操作指令(指令篇結束)

路过秋天發表於2024-04-19

前言:

經過前面幾篇的學習,我們瞭解到指令的大概分類,如:

引數載入指令,該載入指令以 Ld 開頭,將引數載入到棧中,以便於後續執行操作命令。

引數儲存指令,其指令以 St 開頭,將棧中的資料,儲存到指定的變數中,以方便後續使用。

建立例項指令,其指令以 New 開頭,用於在執行時動態生成並初始化物件。

方法呼叫指令,該指令以 Call 開頭,用於在執行時呼叫其它方法。

支條件指令,該指令通常以 Br、或 B、C 開頭,用於在執行分支條件時跳轉指令。

型別轉換指令,該指令通常以 Cast、Conv 開頭或box結尾,用於在執行時對型別進行轉換。

本篇介紹運算操作指令,介紹完後,將結束指令篇。

第六部分:IL指令完整大綱目錄如下:

.NET Emit 入門教程:第六部分:IL 指令:1:概要介紹

.NET Emit 入門教程:第六部分:IL 指令:2:詳解 ILGenerator 輔助方法

.NET Emit 入門教程:第六部分:IL 指令:3:詳解 ILGenerator 指令方法:引數載入指令

.NET Emit 入門教程:第六部分:IL 指令:4:詳解 ILGenerator 指令方法:引數儲存指令

.NET Emit 入門教程:第六部分:IL 指令:5:詳解 ILGenerator 指令方法:建立例項指令

.NET Emit 入門教程:第六部分:IL 指令:6:詳解 ILGenerator 指令方法:方法呼叫指令

.NET Emit 入門教程:第六部分:IL 指令:7:詳解 ILGenerator 指令方法:分支條件指令

.NET Emit 入門教程:第六部分:IL 指令:8:詳解 ILGenerator 指令方法:型別轉換指令

.NET Emit 入門教程:第六部分:IL 指令:9:詳解 ILGenerator 指令方法:運算操作指令(指令篇結束)

運算操作指令介紹:

在.NET Emit 程式設計中,運算操作指令是一類關鍵的IL(Intermediate Language)指令,用於在動態生成的程式碼中執行各種數學運算、位操作和比較操作。

這些指令允許開發人員對運算元進行加法、減法、乘法、除法、邏輯與、邏輯或、邏輯非、位與、位或、位異或、左移、右移以及比較等操作。

透過運算操作指令,開發人員能夠在動態生成的程式碼中實現各種算術運算、邏輯運算和位操作,從而更靈活地處理資料和實現複雜的邏輯。

這些指令為動態程式碼生成提供了強大的功能,使得開發人員能夠根據需要生成高效且功能豐富的程式碼。

運算操作指令的分類:

讓我們按照分類逐一介紹各種指令以及它們的詳細用途。

  1. 算術運算指令:

    • add(加法):將兩個值相加,並將結果推送到計算棧上。主要用於執行整數和浮點數的加法操作。
    • sub(減法):將一個值減去另一個值,並將結果推送到計算棧上。用於執行整數和浮點數的減法操作。
    • mul(乘法):將兩個值相乘,並將結果推送到計算棧上。用於執行整數和浮點數的乘法操作。
    • div(除法):將一個值除以另一個值,並將結果推送到計算棧上。用於執行整數和浮點數的除法操作。
  2. 邏輯運算指令:

    • and(與):對兩個整數進行按位與操作,並將結果推送到計算棧上。用於執行邏輯與操作。
    • or(或):對兩個整數進行按位或操作,並將結果推送到計算棧上。用於執行邏輯或操作。
    • xor(異或):對兩個整數進行按位異或操作,並將結果推送到計算棧上。用於執行邏輯異或操作。
  3. 位操作指令:

    • shl(左移):將一個整數向左移動指定的位數,並將結果推送到計算棧上。用於執行左移操作。
    • shr(右移):將一個整數向右移動指定的位數,並將結果推送到計算棧上。用於執行算術右移操作。
    • not(非):對一個整數進行按位取反操作,並將結果推送到計算棧上。用於執行按位取反操作。
  4. 比較操作指令:

    • ceq(相等比較):比較兩個值是否相等,並將結果推送到計算棧上。用於執行相等比較操作。
    • clt(小於比較):比較一個值是否小於另一個值,並將結果推送到計算棧上。用於執行小於比較操作。
    • cgt(大於比較):比較一個值是否大於另一個值,並將結果推送到計算棧上。用於執行大於比較操作。

這些指令提供了豐富的功能,可以用於執行各種數學運算、邏輯運算、位操作和比較操作,從而實現各種複雜的程式設計邏輯。在動態生成的程式碼中,開發人員可以根據具體需求使用這些指令來實現所需的功能。

接下來,我們對一個指令分類,分別給出一個示例,來介紹它們的基本用法。

對於執行指令,有兩個指令字尾:

_ovf: 進行溢位檢查。

_un:無符號。

例如:

多數指令都帶有這兩個字尾,理解這兩個字尾的意思,可以快速理解所有該字尾指令。

1、算術運算指令:

Add 指令:

示例程式碼:

 MethodBuilder methodBuilder = tb.DefineMethod("MathTo", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int),typeof(int) });

 ILGenerator il = methodBuilder.GetILGenerator();

 il.Emit(OpCodes.Ldarg_0);
 il.Emit(OpCodes.Ldarg_1);
 il.Emit(OpCodes.Add_Ovf_Un);

 il.Emit(OpCodes.Ret);     // 返回該值

對應程式碼:

其它指令使用方式一樣,省去重複舉例。

Add 指令對應C#程式碼:+
Sub 指令對應C#程式碼:-
Mul 指令對應C#程式碼:*
Div 指令對應C#程式碼:/

2、邏輯運算指令:

示例程式碼:

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.And);

il.Emit(OpCodes.Ret);     // 返回該值

對應程式碼:

使用方式和算術指令執行其實一致:

 And 指令對應C#程式碼:&
  Or  指令對應C#程式碼:|
 Xor  指令對應C#程式碼:^

3、位操作指令:

Shl 指令:左移指令,Shift Left 的簡寫

示例程式碼:

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4,2);
il.Emit(OpCodes.Shl);

il.Emit(OpCodes.Ret);     // 返回該值

對應程式碼:

使用方式,需要在第二個引數,指定要位移的位數。

右移操作的方式和左移一樣。

Not 指令:按位取反

示例程式碼:

MethodBuilder methodBuilder = tb.DefineMethod("MathTo", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int) });

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Not);

il.Emit(OpCodes.Ret);     // 返回該值

對應程式碼:

右移操作的方式和左移一樣,而 Not 指令則不需要第二個引數:

Shl 指令對應C#程式碼:<<
Shr 指令對應C#程式碼:>>
Not 指令對應C#程式碼:~

4、比較操作指令:

Ceq 指令:比較兩個值

示例程式碼:

var dynamicMethod = new DynamicMethod("ConvertTo", typeof(bool), new Type[] { typeof(int), typeof(float) }, typeof(AssMethodIL_Condition));

ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ceq);

il.Emit(OpCodes.Ret);     // 返回該值

var result = dynamicMethod.Invoke(null, new object[] { 11, 11 });
Console.WriteLine(result);
Console.Read();

對應程式碼:

執行結果:

其它兩個指令使用方式和 Ceq 一致:

Ceq 指令對應C#程式碼:==
Clt 指令對應C#程式碼:<
Cgt 指令對應C#程式碼:>

如何實現 >= 或 <=

由於沒有對應的指令,所以需要用點小技巧組合,來實現該程式碼:

用Clt + Ceq 指令實現:>=

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Clt);

il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ceq);

il.Emit(OpCodes.Ret);     // 返回該值

對應程式碼:

用Cgt + Ceq 指令實現:<=

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Cgt);

il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ceq);

il.Emit(OpCodes.Ret);     // 返回該值

對應程式碼:

效能小細節提醒:

平時我們寫程式碼,涉及 >= 或 <= 的整數字判斷時候:

比如:a>=2(需要兩條指令),可以寫成 a>3(只要一條指令)。

總結:

在.NET Emit程式設計中,我們探討了運算操作指令的重要性和應用。

這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的程式碼中實現對資料的處理和操作。

透過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯。

在實際應用中,我們可以利用這些指令來實現諸如加密演算法、數值計算、邏輯判斷、資料壓縮等功能。

透過深入理解和熟練運用這些運算操作指令,開發人員可以提高動態程式碼生成的效率和靈活性,從而更好地滿足各種程式設計需求。

同時,對ILGenerator指令方法的進一步學習也能夠幫助開發人員更加靈活地控制動態生成的程式碼,實現更復雜的功能和邏輯。

本篇之後,將進入第七部分:實戰專案

相關文章