无效的 IL 程序,我做错了什么? (简单的 if-else 代码)
Invalid IL program, what am I doing wrong? (simple if-else code)
我正在学习 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[=11=]00
IL_000F: ldloc.2 // CS[=11=]00
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
有一件事我不太明白,觉得多余的是 brtrue.s
之前的 stloc.2
和 ldloc.2
- 为什么我们需要存储和加载我们得到的值从 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
我认为您需要先调用 DeclareLocal()
声明您的局部变量,然后才能使用它们。
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
我正在学习 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[=11=]00
IL_000F: ldloc.2 // CS[=11=]00
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
有一件事我不太明白,觉得多余的是 brtrue.s
之前的 stloc.2
和 ldloc.2
- 为什么我们需要存储和加载我们得到的值从 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
我认为您需要先调用 DeclareLocal()
声明您的局部变量,然后才能使用它们。
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