2017-06-20 58 views
1

我正在C#中构建一个小型编译器,因此不得不插手动态程序集并发布操作码。现在,奇怪的是我的Emit()调用在生成的模块中创建了额外的nop操作码。对我而言,这并不是很重要,因为表演并不是真正的关键,但它真的让我感到困惑,为什么会发生这种情况。这似乎是加载或存储到本地人或参数后发生。任何C#/动态组装专家,可以指向我可以检查的事情?我附上了生成的代码示例,如果需要更多信息,请让我知道。谢谢。为什么ILGenerator.Emit()在动态程序集中插入nop操作码?

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。

+4

看到[this](https://stackoverflow.com/questions/1498162/c-sharp-ilgenerator-nop),可能是你的情况。错误的编码也许。 – Mat

+0

该方法如何发出的示例代码会很有帮助。 – IllidanS4

+0

刚刚更新了我的问题 – user1171946

回答

3

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

要么将​​Index的类型更改为short,要么将其更改为Emit

相关问题