c#ILGenerator nop?

Pet*_*ter 6 c# cil

我在这里使用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);
Run Code Online (Sandbox Code Playgroud)

这产生了这个IL:

IL_0000:  ldarg.0    
IL_0001:  ldarg.1    
IL_0002:  ldc.i4.s   100
IL_0004:  nop        
IL_0005:  nop        
IL_0006:  nop        
Run Code Online (Sandbox Code Playgroud)

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

从哪里开始编码?有没有办法摆脱它们?我试图模仿一些c#代码,它没有3个nops.

T. *_*bre 8

你正朝着正确的方向摆脱"nop":

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

对于OpCodes.Ldc_I4_S,MSDN声明:

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

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

ILGenerator.Emit(OpCode,byte)

所以你的代码的第二部分在运行时会产生不可预测的结果(除了那些讨厌的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);
}
Run Code Online (Sandbox Code Playgroud)

如果要将int32/short(或任何大于一个字节的值)正确加载到堆栈中,则应使用Ldc_I4而不是Ldc_I4_S.所以你的代码应该是这样的而不是上面的示例:

else
{
    gen.Emit(OpCodes.Ldc_I4, value);
}
Run Code Online (Sandbox Code Playgroud)

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

希望有帮助......

  • 是的,当你使用int重载而不是字节重载时,当操作码需要1个字节的参数时,生成器(盲目地看起来)会将参数作为4字节整数发出.因为它是以小端发出的,所以数字是一个小于128的正值,而nop指令恰好是一个'0x00'字节,它具有添加3个nop指令的效果.如果整数恰好是128,则它将0加载到堆栈上,第一个nop指令将被替换为break指令. (2认同)