无效的 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.2ldloc.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