簡體   English   中英

為什么ILGenerator.Emit()在動態匯編中插入nop操作碼?

[英]Why does ILGenerator.Emit() insert nop opcodes in dynamic assembly?

我正在用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操作碼,您應該使用接受shortEmit重載作為第二個參數,而您的Index大概是int ,因此使用了錯誤的Emit重載。 IL生成器不會對此進行檢查,僅將值的所有4個字節輸出到IL流。 2個高位字節為零,在IL中為nop ,因此在反匯編中為nop

Index的類型更改為short或在傳遞給EmitEmit將其Emit

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM