为什么ILGenerator.Emit()在动态程序集中插入nop操作码?

use*_*946 2 .net cil ilasm nop emit

我正在用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
Run Code Online (Sandbox Code Playgroud)

根据要求,下面是我的代码的概述.有些东西缺失,因为代码被分成单独的模块,这些是执行顺序中最相关的部分.

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);
}
Run Code Online (Sandbox Code Playgroud)

对于我的抽象语法树上的每个节点(用三个地址代码装饰),我只是这样做:

public override void DefaultPost(NodeBase n)
{
    foreach (Quad q in n.Tac)
        q.Emit(Cil);
}
Run Code Online (Sandbox Code Playgroud)

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

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);

...
Run Code Online (Sandbox Code Playgroud)

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

http://github.com/yannikab/grc

与目标代码生成相关的所有内容都在Cil名称空间下.将所有内容放在一起用于代码生成的类名为CilVisitor.

Iri*_*ium 5

正如评论指出,对于Ldarg,StlocLdloc操作码,你应该使用Emit一个接受过载short作为第二个参数,而你Index大概是一个int,所以错了Emit正在使用的过载.IL生成器不会检查这一点,只是将值的所有4个字节输出到IL流.2个高位字节为零,nop在IL中,因此nop在反汇编中为s.

要么改变类型Indexshort或路过时施放它Emit.