我有这段代码发出一些 IL
指令,这些指令在 null
对象上调用 string.IndexOf
:
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
"Foo",
MethodAttributes.Public,
typeof(void), Array.Empty<Type>());
var methodInfo = typeof(string).GetMethod("IndexOf", new[] {typeof(char)});
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldnull);
ilGenerator.Emit(OpCodes.Ldc_I4_S, 120);
ilGenerator.Emit(OpCodes.Call, methodInfo);
ilGenerator.Emit(OpCodes.Ret);
这是生成的 IL
代码:
.method public instance int32 Foo() cil managed
{
// Code size 12 (0xc)
.maxstack 2
IL_0000: ldnull
IL_0001: ldc.i4.s 120
IL_0003: nop
IL_0004: nop
IL_0005: nop
IL_0006: call instance int32 [mscorlib]System.String::IndexOf(char)
IL_000b: ret
} // end of method MyDynamicType::Foo
如您所见,在 call
指令之前有三个 nop
指令。
首先我想到了调试/发布构建,但这不是编译器生成的代码,我发出原始 IL 代码并希望按原样看到它。
所以我的问题是当我没有发出任何指令时为什么会有三个 nop
指令?
最佳答案
ILGenerator
不是很高级,如果您使用 Emit(OpCode, Int32)
重载它会将整个 int32
放入指令中流,无论操作码是 Ldc_I4
(实际上需要 4 个字节的立即数)还是 Ldc_I4_S
(不需要)。
因此请确保使用正确的重载:
ilGenerator.Emit(OpCodes.Ldc_I4_S, (byte)120);
lemmas for the opcodes在文档中指定 Emit
的哪个重载是正确的。
在reference source ,带有 int
参数的 Emit
这样做:
public virtual void Emit(OpCode opcode, int arg)
{
// Puts opcode onto the stream of instructions followed by arg
EnsureCapacity(7);
InternalEmit(opcode);
PutInteger4(arg);
}
PutInteger4
将四个字节写入构建 IL 的字节数组。
Emit
的文档说额外的字节将是 Nop
指令,但这只是在它们实际上为零的情况下。如果传递的值“更错误”(高字节不为零),那么效果可能会更糟,从无效的操作码到巧妙地破坏结果的操作。
关于c# - 为什么 IL.Emit 方法要添加额外的 nop 指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52349954/