6

我使用 ILGenerator 生成一些 IL,这是我的代码:

DynamicMethod method = new DynamicMethod("test", null, Type.EmptyTypes);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ldc_I4_S, 100);

这产生了这个 IL:

IL_0000:  ldarg.0    
IL_0001:  ldarg.1    
IL_0002:  ldc.i4.s   100
IL_0004:  nop        
IL_0005:  nop        
IL_0006:  nop        

(我从名为 ILStream 的 VS Virtulizer 获得 IL 代码)

nops 代码从哪里来?有没有办法摆脱它们?我试图模仿一些 c# 代码,但它没有 3 个 nops。

4

2 回答 2

8

你是在正确的方向摆脱“nop”:

当您为 Emit 调用提供附加参数时,请务必在 MSDN 上检查正确的参数类型。

对于 OpCodes.Ldc_I4_S,MSDN 指出:

ldc.i4.s 是一种更有效的编码,用于将整数从 -128 推到 127 到 >evaluation 堆栈。

以下 Emit 方法重载可以使用 ldc.i4.s 操作码:

ILGenerator.Emit(操作码,字节)

因此,您的代码的第二部分将在运行时产生不可预测的结果(除了那些讨厌的 nop),因为您试图在堆栈上加载“int8”,但提供“int32”或“short”值:

else if (IsBetween(value, short.MinValue, short.MaxValue))
{
    gen.Emit(OpCodes.Ldc_I4_S, (short)value);
}
else
{
    gen.Emit(OpCodes.Ldc_I4_S, value);
}

如果您想将 int32/short(或任何大于字节的大小)正确加载到堆栈上,则应使用 Ldc_I4 而不是 Ldc_I4_S。因此,您的代码应如下所示,而不是上面的示例:

else
{
    gen.Emit(OpCodes.Ldc_I4, value);
}

这是一个疯狂的猜测,但是生成的三个 nop 可能与 int32 中的额外字节有关

希望有帮助...

于 2011-11-15T09:23:52.187 回答
3

我通过将 int 转换为一个值解决了这个问题:

代码:

private static bool IsBetween(int value, int min, int max)
{
    return (value >= min && value <= max);
}

private static void WriteInt(ILGenerator gen, int value)
{
    gen.Emit(OpCodes.Ldarg_1);
    if (IsBetween(value, sbyte.MinValue, sbyte.MaxValue))
    {
        gen.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
    }
    else if (IsBetween(value, byte.MinValue, byte.MaxValue))
    {
        gen.Emit(OpCodes.Ldc_I4_S, (byte)value);
    }
    else if (IsBetween(value, short.MinValue, short.MaxValue))
    {
        gen.Emit(OpCodes.Ldc_I4_S, (short)value);
    }
    else
    {
        gen.Emit(OpCodes.Ldc_I4_S, value);
    }
}
于 2009-09-30T15:01:30.500 回答