HLX-001 の補足ページ#2
(8) HLX-001の最適化アルゴリズム
- HLX-001は、いろいろな最適化を比較的コンパクトに実現できていると思うのですが、そのアルゴリズムの詳細や、なぜそういう仕様にしたのかの説明を書いておけば、誰かの参考になるかもしれないと思ったので書いておきます。
- [5-1] 「比較演算子の結果を0かどうか比較する」という処理を一度の比較にまとめます。
- AEs_optimizeSub0() [24行] のアルゴリズム:
最適化の範囲: p0 ~ p1
for (p = p0; p < p1; p = 次の命令) {
if (pの命令がCmpEq~CmpRnz) { r = p; (この命令の場所を覚えておく) }
if (pの命令がSetCc && 次の命令がCmpEqかCmpNeで定数0との比較 && SetCcの結果をその先で一切使わない) {
if (次の命令がCmpEq) { rに書いてある命令の比較条件を反転させる }
pのSetCc命令をNopにしてしまう
pの次のCmpEq/CmpNe命令もNopにしてしまう
}
}
- [例1]
if ((a > 1) != 0) goto label; // HLX-001が内部的に生成する共通中間コードをC言語風に書いたもの
1: CmpGt(a, 1); // (最適前)上記を共通中間コードで表したもの
2: SetCc(tmp); // この2行目と3行目は最適化によって消される
3: CmpNe(tmp, 0);
4: Jcc(label);
1: CmpGt(a, 1); // (最適化後)このようにうまくいく
2: Jcc(label);
- [Q] 「SetCcの結果をその先で一切使わない」ってどうやって判定していますか?
- [A] プログラムでは AEs_isVoid() で判定しています。
- やっていることは単純で、先の命令を一つずつ順番に見ていって、もしその変数が何らかの命令の引数に使われていれば、それは「使われている」と判断してそこでおしまいです。
- もしその変数に対して何らかの値が上書きされるか、OpVoid命令で無効化されたら、それは「使われていない」と判断しておしまいです。
- JmpやJccに出会ってしまったら、その先でどうなるかは分からないので、「使われている」ということにしておしまいです。・・・いやJmpであればさらに追跡も可能そうではありますが、それで最適化ルーチンが無限ループに入ったらいやだなとか、それを回避するためにどの命令がチェック済みかを管理するのも手間だなと思って、やっていません。
- 私としては、そこを頑張らせるよりは、「a = void;」みたいな命令をHLX-001にサポートさせて、プログラム内で「この変数はこの先で値を使わないよ」って明示できるようにしたいです。そうすればコンパイラが追跡を頑張らなくても、同等の最適化結果が得られるようになるので。
- [5-2] 「定数畳み込み」と「定数伝播」をやります。
- [5-3] 分岐命令で分岐した先が無条件分岐命令だったら、最初からそこに行くように分岐先を修正します。
- [5-4] 宣言したけど使われていないラベルを除去します。
- [5-5] デッドコード(一度も実行されることのないコード)があればそれをすべて消します。
- [5-6] 分岐先が次の命令を指している場合、その分岐命令はNOPと同じなので除去します。
- [5-7] 演算した結果がその先で一切利用されないことが確実な場合、その演算をする必要はないので、削除してしまいます。
- [5-8] HLX-001ではfor文の前に unroll(11) みたいなプリフィクスを付けるとループアンロールができます。
こめんと欄
|