パート3/3。 HaskellのICFPC 2009に理想的なVMコンパイラーであり、コメントを広めています

終了 前のパーツ: 1および2



他に何が残っていますか? 隣接する2つのオペコードが1つになる場所を逃しました。 何があったの? 仕様に従って、次の操作が行われました。



flag = m20 > 0

if (flag) m222 = m3 else m222 = m4









これらのコンストラクトがどのように使用されているかをバイナリファイルで調べると、Cmp、Phiのペアになっていることがわかりました。 確かに、それはある種のコンパイラーによって元の式のいくつかからオーガナイザーによってコンパイルされたためです。 そうだとすれば、1回の操作でそれらを少し接着します。



-- convert 2-op conditional operator to 1-op <br>

removePhi [] = []<br>

removePhi ((Cmpz cond condr1) : (Phi addr r1 r2) : xs) = <br>

Noop addr : If cond condr1 addr r1 r2 : removePhi xs<br>

removePhi (x : xs) = x : removePhi xs<br>





ここで、リストは次のように管理されます。オペコードのリストが関数の入力に提供されます;出力では、このペア[Cmp、Phi]が[Noop、If]に置き換えられたリストがあります。 Noopは、すべてのコマンドのアドレスを変更しないままにするために挿入されます(つまり、メモリ内の位置がオペランドの1つであるため、それらの数と位置が変更されないように)。



ここで、空のリストの場合、何もする必要がないと判断します。 探している2つのチームから始まるリストについては、それらを2つの新しいチームに置き換えてから、残りのリストを処理します。 そして、リストが他のすべてで始まる場合(3行目に対応し、残りには「x」という名前が付けられます)、この残りは変更せずに残り、リストの残り「xs」を処理します。



今、プログラムの主要部分。 Haskellエントリポイント-主な機能



main = do <br>

(len,buf) <- readMyFile<br>







これはファイルを読み取ります(上記を参照)

let nframes = fromInteger $ len `div` 12 -- . <br>

-- opcode/data memory in tuples <br>

ids <- mapM ( \ i -> instruction (plusPtr buf $ i * 12 ) i) [ 0 .. nframes - 1 ]<br>





ここでは、各i(0からnframes-1まで)が結果にマッピングされました。これは、対応するオフセット(i * 12)をポインター(buf)に追加し、このアドレスでコマンドを解釈する(ポインターで命令を呼び出す偶数のサンプリングのために、それがどこにあるか、命令番号とともに)。



-- code AST <br>

let code = removePhi $ map disasm $ zip (map fst ids) [ O.. ]<br>





ここで(右側を読んで)、ペア(id)のリストからすべて(map)first(fst)の要素(命令のみ)を取得し、ゼロから必要な数のペア(zip)を作成しました([0 ..]) )、これらすべてのペア(命令、アドレス)について、disasmを通過(マップ)し、Opsのリストを取得し、それらからPhiを削除し、代わりにIfを作成します(removePhiを使用)。



-- print code<br>

putStrLn $ produceCode code (map snd ids)<br>





さらに、コードとデータ(map snd idを介して-ペアの後半(「命令、データ)」を取得)を使用して、2つのパラメーターでproduceCodeを呼び出し、結果(putStrLn)を出力しました。



free buf





そして、この行は間違いなくコメントを必要としません。



おわりに



もちろん、この「コンパイラ」は初心者のHaskelistのレベルで書かれています。 しかし、このレベルでさえ、私は仕事中に毎日書くJavaとは著しく異なりました。



第一に、Javaが信頼できるジャビストが使用する可能性のあるあらゆる種類のパターンを実装するために必要な「ユーティリティ関数」とクラスがないことです。



第二に、時間の分布。 8バイトをDoubleに変換するこの特定の方法を選択したため、Ptrを使用した研究に多くの時間を費やしました。 次に、ファイルのデコードの作成にかかった時間の約3分の2、ASTおよびHaskellコードでの基本的なシリアル化(線形)を作成しました。 そして、フラットオペコードをツリーのようなASTに変換するためのアルゴリズムに実際に費やした時間の3分の1だけで、アルゴリズム自体が判明しました。



要するに、私は深い満足感を経験しました。 IFCPCでこれを行う時間がなかったのは残念です。



All Articles