#topicpath
#contents

コンパイラ作成を支援するライブラリです。ライブラリはC#のマネージドコードですが、出力するのはネイティブバイナリです。([[関連日記>http://gir-lab.spaces.live.com/blog/cns!63A4C32EDA5CF2F7!487.entry]])

** &color(red){注意事項}; [#rc2aec75]

- C#のネイティブコンパイラではありません。C#で作ったネイティブコンパイラ作成用のサポートライブラリです。
-- 言語処理系を含まないため、特定の言語(C言語、C#等)をコンパイルすることはできません。
-- 逆に言うと、言語処理系だけを作って下層のリンク処理等をCompilerLibに丸投げすれば、PEの細かいことを気にせずにネイティブコンパイラを作ることができます。CompilerLibはそのためのライブラリです。
-- &color(red){''【追記】''};言語処理系として[[LLPML]]の開発を開始しました。
- C#で作ったプログラムを.NETなしにネイティブで動かすように変換することは&color(red){''できません''};。将来的にサポートする予定もありません。C#のAoTコンパイラを期待された方にはごめんなさい。
-- 応用でC#のAoTコンパイラを作ることは可能です。原理的には[[PE Analyzer]]でCILを抜き出して、x86コードを生成してCompilerLibに渡せばできるはずです。ただし説明は簡単ですがやるととんでもなく難しいので、現時点では開発する予定はありません。

** 言語処理系 [#u4ccb418]

CompilerLibを利用した言語処理系です。
- [[LLPML]]

** ダウンロード [#jdb13268]

//&color(red){''【注意】''};[[LLPML]]には以下のものより新しいCompilerLibが含まれています。LLPMLの開発が落ち着いてから、こちらにもバックポートする予定です。
- ソース: &ref(CompilerLib-0.6-20080825.zip); - [[LLPML]] 1.0からバックポート ⇒ [[関連日記>http://gir-lab.spaces.live.com/blog/cns!63A4C32EDA5CF2F7!868.entry]]
- ライセンス: パブリックドメイン

#include(:VCS2008Exp,notitle)

*** 過去のリリース [#s916288a]
- ソース: &ref(CompilerLib-20070114.zip); - ラベルをサポート
- ソース: &ref(CompilerLib-20070108.zip); - 簡単なEXEを出力

** 今後の課題 [#cdae3d41]
+ アセンブラの充実
-- [[LLPML]] 1.0で必要になったニーモニックとオペランドの組み合わせしかサポートされていません。
-- MMX/SSE/SSE2命令はある程度実装されていますが、FPU命令はまったく実装されていません。
-- まともに使うには整備する必要があります。
+ 【済】%%ラベル%%
//-- CompilerLibではアセンブリのラベルが扱えないため、条件分岐やループができません。オブジェクトベースで表現できるようにする必要があります。
+ 自分自身の記述
-- C#での機能拡張はほどほどにして、言語処理系を実装して自分自身を記述します。 ⇒ [[WikiPedia.en:Eating one's own dog food]]
+ x64対応
-- いまどきこんなレイヤで作業をするなら、そろそろ手を出す時期でしょう。

** 例 [#naa680e1]
コンパイラの内部処理用ライブラリを想定しているため、言語処理系を含んでいません。テキストではなくオブジェクトを渡します。アセンブリをコードで組み立てて出力するというイメージです。

以下のC言語と同等のものを作成します。
 #include <windows.h>
 
 int main()
 {
     HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
     const char *hello = "Hello, World!\r\n";
     DWORD dummy;
     WriteConsole(stdout, hello, strlen(hello), &dummy, NULL);
     return 0;
 }

CompilerLibを利用したコードは以下のようになります。
 var module = new Module();
 var c = new List<OpCode>();
 
 const int STD_OUTPUT_HANDLE = -11;
 var GetStdHandle = module.GetFunction(CallType.Std, "kernel32.dll", "GetStdHandle");
 var WriteConsole = module.GetFunction(CallType.Std, "kernel32.dll", "WriteConsoleW");
 var ExitProcess = module.GetFunction(CallType.Std, "kernel32.dll", "ExitProcess");
 
 // HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
 var stdout = new Addr32(module.GetInt32("stdout"));
 c.AddRange(GetStdHandle.Invoke(STD_OUTPUT_HANDLE));
 c.Add(I386.Mov(stdout, Reg32.EAX));
 
 var dummy = module.GetInt32("dummy");
 var hello = "Hello, World!\r\n";
 c.AddRange(WriteConsole.Invoke(stdout, hello, hello.Length, dummy, 0));
 c.AddRange(ExitProcess.Invoke(0));
 
 module.Text.OpCodes = c.ToArray();
 module.Link("output.exe");

** コード記述方法 [#d592462d]
CompilerLibではオブジェクトベースでコードを記述します。

*** 変数 [#a79ec7ac]
Moduleインスタンスからグローバル変数のポインタを取得するのがModule.GetInt32()です。ポインタが指す値はAddr32クラスを通して扱います。C++と対比させたイメージを示します。

 【C++】
 extern int g_abc;
 int *p_abc = &g_abc;
 int &abc = *p_abc;
 
 【CompilerLib】
 var p_abc = module.GetInt32("g_abc");
 var abc = new Addr32(p_abc);

アセンブラレベルでは、p_abcがEDXに割り当てられた場合、abcは[EDX]に相当します。アセンブリの[]で括ったアドレッシングがnew Addr32()に相当します。
 【アセンブリ】 EDX → [EDX]
 【C言語】 edx → *edx
 【CompilerLib】 edx → new Addr32(edx)

この操作はCompilerLibでオブジェクト化されたアセンブリに反映されます。
 // mov eax, [0x12345678]
 I386.Mov(Reg32.EAX, new Addr32(0x12345678));
 // mov edx, 0x12345678
 I386.Mov(Reg32.EDX, 0x12345678);
 // mov eax, [edx]
 I386.Mov(Reg32.EAX, new Addr32(Reg32.EDX));

//【事実誤認のため削除】%%最後の例で[EDX]がnew Addr32(Reg32.EDX)のように対称形になっていないのは、オペランドに許されている組み合わせがレジスタ経由と即値では異なるためです。別の型になっていればI386.Mov()のオーバーロードで表現することができます。たとえばmov [0x12345678], 0x12345678のような組み合わせをコンパイル時に排除する一方で、mov [EDX], 0x12345678のような組み合わせを許容することができます。%%

*** ラベル [#xd90f10f]
以下のアセンブリと同等のものを作成します。
XORやLOOPでコードを節約できますが、ここでは使用しません。

 	mov eax, 0
 	mov ecx, 100
 label:
 	add eax, ecx
 	dec ecx
 	jnz label

CompilerLibを利用したコードは以下のようになります。
 var c = new List<OpCode>();
 c.Add(I386.Mov(Reg32.EAX, 0));
 c.Add(I386.Mov(Reg32.ECX, 100));
 var label = new OpCode();
 c.Add(label);
 c.Add(I386.Add(Reg32.EAX, Reg32.ECX));
 c.Add(I386.Dec(Reg32.ECX));
 c.Add(I386.Jnz(label.Address));

デフォルトコンストラクタで空のOpCodeを作成して、
アドレスを取得するためだけに使用しています。

** コメント [#m2d815ec]

#comment(below)

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS