IL程序无效,我做错了什么? (简单的if-else代码)

时间:2015-02-16 16:17:59

标签: c# il

我正在学习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.2brtrue.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

2 个答案:

答案 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