c# - 为什么 IL.Emit 方法要添加额外的 nop 指令?

标签 c# cil reflection.emit ilgenerator

我有这段代码发出一些 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/

相关文章:

c# - 如何卸载使用 RunAndCollect 创建的动态程序集?

c# - 如何发出返回 ref 的动态方法?

c# - .Net核心MySql

c# - 在 C# 单元测试中比较两个 List<string[]> 对象

c# - 当我试图解析的文本框中没有输入任何内容时,应用程序崩溃

c# - 计算加速文件中的播放秒数

.net - 为什么 .NET Core 和 .NET 5 会生成可执行文件?

c# - 将 lambda 作为 IL 流传递给辅助 AppDomain,然后使用 DynamicMethod 将其组装回去

c# - TypeBuilder 的 Type.GetMethod 替代方案

c# - 如何将CIL代码插入到C#中