2017-06-20 4 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 

ここでは、私のコードの外観を要約しています。いくつか欠けているものがあり、コードが別々の モジュールに分割されているため、これらは実行される順序で最も関連性の高い部分です。 (3番地コードで飾られた)私の抽象構文木の各ノードに対して

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

コメントで示されているように、あなたのIndexはおそらくintですので、間違ったEmit過負荷がされているのに対し、LdargStlocLdlocオペコードのために、あなたは、2番目のパラメータとしてshortを受け入れEmitオーバーロードを使用する必要があります中古。 ILジェネレータはこれをチェックせず、値の4バイトすべてをILストリームに出力するだけです。 2つの上位バイトは0です。これはILでnopです。逆アセンブリではnopです。

Indexのタイプをshortに変更するか、Emitに渡すときにキャストします。

関連する問題