我正在学习IL。我发现LINQpad实际上非常适合编写C#代码并立即查看生成的IL。比VS / ILSpy快得多。
我写了这个简单的代码:
int x = 10;
int y = 20;
if (x > y)
Console.WriteLine("X is greater");
else
Console.WriteLine("y is greater");
得到了:
IL_0001: ldc.i4.s 0A
IL_0003: stloc.0 // x
IL_0004: ldc.i4.s 14
IL_0006: stloc.1 // y
IL_0007: ldloc.0 // x
IL_0008: ldloc.1 // y
IL_0009: cgt
IL_000B: ldc.i4.0
IL_000C: ceq
IL_000E: stloc.2 // CS$4$0000
IL_000F: ldloc.2 // CS$4$0000
IL_0010: brtrue.s IL_001F
IL_0012: ldstr "X is greater"
IL_0017: call System.Console.WriteLine
IL_001C: nop
IL_001D: br.s IL_002A
IL_001F: ldstr "y is greater"
IL_0024: call System.Console.WriteLine
在stloc.2
之前,ldloc.2
和brtrue.s
为什么我还没有完成并感到多余的一件事 - 为什么我们需要存储和加载我们得到的值从ceq
返回,因为它已被ceq
推送到堆栈中?
所以我自己试了一下,没有做那两个命令:
static void Main()
{
var asm_name = new AssemblyName("Asm");
var asm_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(asm_name, AssemblyBuilderAccess.RunAndSave);
var mod_builder = asm_builder.DefineDynamicModule("Mod", "Test.dll");
var type_builder = mod_builder.DefineType("Test", TypeAttributes.Public);
var cmp_builder = type_builder.DefineMethod("Compare",
MethodAttributes.Public | MethodAttributes.HideBySig,
CallingConventions.Standard,
typeof(void),
Type.EmptyTypes);
var WL = typeof(Console).GetMethod("WriteLine",
BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null);
// int x = 10;
// int y = 20;
// if (x > y)
// WL("x is greater");
// else
// WL("y is greater");
var il = cmp_builder.GetILGenerator();
var ygtX = il.DefineLabel(); // defines a label for y > x
il.Emit(OpCodes.Ldc_I4, 0xA); // push 10
il.Emit(OpCodes.Stloc_0); // pop 10 to location 0 (x)
il.Emit(OpCodes.Ldc_I4, 0x14); // push 20
il.Emit(OpCodes.Stloc_1); // pop 20 to location 1 (y)
il.Emit(OpCodes.Ldloc_0); // push x
il.Emit(OpCodes.Ldloc_1); // push y
il.Emit(OpCodes.Cgt); // pops x, y and compares them. pushes 1 if x > y else 0
il.Emit(OpCodes.Ldc_I4_0); // push 0
il.Emit(OpCodes.Ceq); // pops the last two values, pushes 1 if equal else 0
il.Emit(OpCodes.Brtrue_S, ygtX); // pops the last value, branches to ygt label if value is true (== 1)
il.Emit(OpCodes.Ldstr, "x is greater"); // pushes string
il.Emit(OpCodes.Call, WL); // pops string, calls WriteLine with it
il.MarkLabel(ygtX); // marks the beginning of y > x label
il.Emit(OpCodes.Ldstr, "y is greater"); // pushes string
il.Emit(OpCodes.Call, WL); // pops string, calls WriteLine with it
il.Emit(OpCodes.Ret);
var type = type_builder.CreateType();
var print = type.GetMethod("Compare");
var cmp = (Action)Delegate.CreateDelegate(typeof(Action), null, print);
cmp();
asm_builder.Save("Test.dll");
}
运行抛出:
Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program.
添加这两条指令(分支之前的存储和加载,如第一个IL代码段中所示)并没有帮助。
我做错了什么?
感谢您的帮助。
编辑:这是LINQpad的优化输出。更有意义。这两个命令确实是多余的。
IL_0000: ldc.i4.s 0A
IL_0002: stloc.0 // x
IL_0003: ldc.i4.s 14
IL_0005: stloc.1 // y
IL_0006: ldloc.0 // x
IL_0007: ldloc.1 // y
IL_0008: ble.s IL_0015
IL_000A: ldstr "X is greater"
IL_000F: call System.Console.WriteLine
IL_0014: ret
IL_0015: ldstr "y is greater"
IL_001A: call System.Console.WriteLine
答案 0 :(得分:4)
我认为您需要先调用DeclareLocal()
来声明您的局部变量,然后才能使用它们。
答案 1 :(得分:1)
LINQPad不显示本地声明。
尝试添加
var il = cmp_builder.GetILGenerator();
var ygtX = il.DefineLabel(); // defines a label for y > x
il.DeclareLocal(typeof(int)); // add this
il.DeclareLocal(typeof(int)); // and this