FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

「QEMUの仕組み」を探る(実装の詳細について)

引き続き、QEMUの論文を読んでいく。dyngenトランスレータの基本は分かったので、実装の詳細について説明を読み進めていこう。

  • QEMU, a Fast and Portable Dynamic Translator, Fabrice Bellard

http://archives.cse.iitd.ernet.in/~sbansal/csl862-virt/2010/readings/bellard.pdf

3. Implementation Details

3.1 変換ブロックと変換キャッシュ(Translated Block and Translation Cache)

QEMUはジャンプ命令までの一連の命令シーケンスを「変換ブロック(Translated Block)」としてひとつにまとめている。 この変換ブロックを格納するのが変換キャッシュ(Translated Cache)」で、変換したブロックはこのキャッシュに格納され、同じプログラムを実行する場合には再度ターゲットコードの変換を行うことなく、キャッシュの中身を参照して実行することが出来る。

3.2 レジスタ割り当て(Register allocation)

この論文が発表された時点のQEMUは、レジスタ割り当ては固定のものを使用している(今のバージョンは知らない)。 つまり、ターゲットアーキテクチャレジスタは、ホストアーキテクチャの固定したレジスタか、固定した場所のメモリに格納されている。

この方式の利点は実装が簡単なことだが、一方レジスタ割り当てを固定しない方式というのは、メモリ上の任意の場所にターゲットCPUのレジスタを割り当てることである。

この固定しないレジスタ割り当て方式の利点は、例えばレジスタのコピーなど、データの移動をポインタの移動で置き換えることが出来るなど、速度の向上を図ることが出来る点である。

3.3 条件分岐の最適化

ターゲットのCPUのフラグなどを、いちいち計算するのはとても厄介だ。そのためにホスト側の命令もかなりとられてしまうし、大体の場合はフラグを計算しなくても特に問題ないため、不要なことが多い。

このため、QEMUでは「遅延評価(lazy evaluation)」というものを採用している。これは、ターゲットの命令をエミュレートした時点ではフラグなどの計算は行わず、フラグの情報が必要になった時点で、過去の情報を引っ張り出してきて評価する方式である。 このようにすることで、わざわざすべてのフラグを計算することなく、最低限必要なフラグのみを計算するためエミュレーションの速度を向上させることが出来る。

論文中の例では、具体的にどのように遅延評価を実行するのかはいまいち掴めなかったが、だいたいやりたいことは分かった。

CC_SRC=A
CC_DST=R
CC_OP=CC_OP_ADDL

ソースオペランドA、書き込み左記がR、演算がCC_OP_ADDLならば、R=A+Bを再現することが出来、フラグの情報を引き出すことが出来る。

ただしこの場合、Bはどうしたらいいのか?具体的にこれらの情報をどこに格納しておくのか、いまいち分からない。

3.4 ダイレクトブロックチェイン(Direct Block Chaining)

3.1節で、変換ブロックは、ジャンプ命令ごとにひとつのグループになるとしたが、ジャンプ命令に到達すると次のブロックを呼び出してくる必要がある。これにはハッシュテーブルを使う。 もし次のブロックがまだ変換ブロックに格納されていなければ、dyngenを実行して変換を行う。

もし次のジャンプ先が予測できるようなら、次のジャンプ先のブロックを接続してブロックロードを不要にする。これによりブロック転送を削減して高速化を行うことが出来る。

3.5 メモリ管理

QEMUオペレーティングシステムを動作させるためにmmap()を利用してソフトウェアMMUを実現している。

3.5 自己書き換えコードによる変換ブロックの無効化

自己書き換えコードが発生した場合、その書き換え先のコードは、変換ブロック諸共不要になり、変換ブロックを破棄して再度変換を行う。 変換コードの破棄は、リンクリストを利用して効率的に実行できる(ただしこの実装の詳細については論文では語られていない)。

3.7 例外のサポート

longjmp()による例外のサポートを実現する。通常はターゲット命令で例外が発生するとホスト命令でも例外が発生し、ホストPCの例外処理コードが実行されてしまうが、そうならないようにlongjmp()による処理を行っている。

3.8 ハードウェア例外のサポート

ハードウェア例外は、通常のCPUであれば要求があれば自動的に検出され例外ベクタに飛ぶものだが、これをソフトウェアで律儀に実行するとエミュレーション性能に影響を与えてしまう。 これを防ぐために、ハードウェア例外が発生するとQEMUが変換ブロックを変更し、ハードウェア例外が受付待ちであることを示すソフトウェアコードを実行させる。

このソフトウェアコードがエミュレータに例外が発生していることを通知し、すぐさまエミュレータのmainループに戻る。 そしてマインループが例外を検出し、処理に入るというわけだ。

3.9 ユーザモードのエミュレーション

Linuxを動作させるために、QEMUはホストCPU上でのターゲットCPUのユーザモードをエミュレーションする。