为什么 ILGenerator.Emit() 在动态汇编中插入 nop 操作码?



我正在用 C# 构建一个小编译器,所以不可避免地我不得不干预动态程序集和发出操作码。现在,奇怪的是我的 Emit() 调用在生成的模块中创建额外的 nop 操作码。就我而言,这并不重要,因为性能并不是非常关键,但老实说,为什么会发生这种情况,这让我感到困惑。它似乎是在加载或存储到当地人或参数后发生的。任何 C#/动态程序集专家可以指出我可以检查的内容吗?我已经附上了生成的代码的示例,如果需要更多信息,请告诉我。谢谢。

IL_0000:  ldc.i4     0x0
IL_0005:  stloc      c
IL_0009:  nop
IL_000a:  nop
IL_000b:  ldloc      c
IL_000f:  nop
IL_0010:  nop
IL_0011:  stloc      i
IL_0015:  nop
IL_0016:  nop
IL_0017:  ldarg      s
IL_001b:  nop
IL_001c:  nop
IL_001d:  ldloc      i
IL_0021:  nop
IL_0022:  nop
IL_0023:  add
IL_0024:  stloc      $0
IL_0028:  nop
IL_0029:  nop
IL_002a:  ldloc      $0
IL_002e:  nop
IL_002f:  nop
IL_0030:  ldind.i1
IL_0031:  ldc.i4     0x0
IL_0036:  bne.un     IL_0040
IL_003b:  br         IL_008e
IL_0040:  ldloc      c
IL_0044:  nop
IL_0045:  nop
IL_0046:  stloc      $1

根据要求,以下是我的代码外观的概述。缺少一些东西,并且由于代码被分成单独的 模块,这些是执行顺序中最相关的部分。

string programName = "myprogram";
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName(programName), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder module = n.AssemblyBuilder.DefineDynamicModule(programName, string.Format("{0}.exe", programName), true);
string contextName = string.Format("{0}.{1}", programName, "context");

MethodAttributes attributes = MethodAttributes.Private | MethodAttributes.Static;
MethodBuilder methodBuilder = typeBuilder.DefineMethod(method, attributes, returnType, paramTypes);
foreach (string name in paramNames)
methodBuilder.DefineParameter(i++, ParameterAttributes.None, name);
ILGenerator Cil = methodBuilder.GetILGenerator();
...
foreach (var g in qLocals)
{
LocalBuilder localBuilder = Cil.DeclareLocal(type);
localBuilder.SetLocalSymInfo(g.Name);
}
foreach (var s in strings)
{
LocalBuilder localBuilder = Cil.DeclareLocal(typeIndexed.DotNetElementType. MakePointerType());
localBuilder.SetLocalSymInfo(string.Format("_{0}", index));
}
IEnumerable<Quad> jumpTargets =
(from q in n.Tac
select q.Addrs.OfType<AddrQuad>()).
SelectMany(x => x).Select(a => a.Quad).Distinct();
foreach (Quad q in jumpTargets)
q.DefineLabel(Cil);
}

对于我的抽象语法树(用三个地址代码装饰)上的每个节点,我只需执行以下操作:

public override void DefaultPost(NodeBase n)
{
foreach (Quad q in n.Tac)
q.Emit(Cil);
}

这是此函数生成的一系列调用:

cil.Emit(OpCodes.Ldloc, Index);
cil.Emit(OpCodes.Stloc, Index);
cil.Emit(OpCodes.Ldc_I4, (int)this.i);
cil.Emit(OpCodes.Stloc, Index);
cil.Emit(OpCodes.Ldloc, Index);
cil.Emit(OpCodes.Ldc_I4, (int)this.i);
cil.Emit(OpCodes.Br, res.Quad.Label.Value);
cil.Emit(OpCodes.Ldloc, Index);
cil.Emit(OpCodes.Ldc_I4, (int)this.i);
cil.Emit(OpCodes.Stloc, Index);
cil.Emit(OpCodes.Ldloc, Index);
cil.Emit(OpCodes.Stloc, Index);
cil.Emit(OpCodes.Ldloc, Index);
cil.Emit(OpCodes.Ldc_I4, (int)this.i);
cil.Emit(OpCodes.Bge, quad.Label.Value);
cil.Emit(OpCodes.Br, res.Quad.Label.Value);
...

我不知道这是否有帮助,如果您想查看我的完整项目,它位于:

http://github.com/yannikab/grc

与目标代码生成相关的所有内容都在 Cil 命名空间下。将所有内容放在一起进行代码生成的类名为 CilVisitor。

如注释中所述,对于LdargStlocLdloc操作码,您应该使用接受short作为第二个参数的Emit重载,而您的Index可能是int,因此使用了错误的Emit重载。IL 生成器不会检查这一点,只是将值的所有 4 个字节输出到 IL 流。2 个高阶字节为零,这在 IL 中nop,因此在反汇编中nops。

Index类型更改为short或在传递到Emit时强制转换它。

相关内容

  • 没有找到相关文章

最新更新