言語理論とコン
パイラ
第十一回:
コード生成と最適化
http://www.sw.it.aoyama.ac.jp/2005/Language%20Theory%20and%20Compilers/lecture11.html
© 2005 Martin
J. Dürst 青山学院大学
今日の予定
- コード生成 (code generation)
- 最適化 (optimization)
- 学生アンケート
コンパイラの主な段階
字句解析 (lexical analysis)
構文解析 (parsing; syntax analysis)
意味解析 (semantic analysis)
最適化 (optimization)
コード生成 (code generation)
コード生成と最適化の関係
- 構文木で最適化、そこからコード生成
- 生成したコードを分析、最適化
- 実際は両方の組み合わせが多い
コード生成の難しさ
コード生成の手法
- 構文木を辿りながらノードごとにコードを書き出す
- 構文木の構造をコード生成用のパターンのに比べてコードを書き出す
機械の主な種類
- スタック・マシーン:
演算は全てスタックに行われて、バーチャルマシーンに多い
- RISC:
演算は全てレジスタ内に行われて、純粋のロードとストアしかない
- CISC:
命令の数が多くて複雑 (例: Intel Pentium)
スタック・マシーンの式のコード生成
- 構文木を深さ優先に辿る
- 葉を PUSH 命令に変更
- 演算ノードをスタック上の演算命令に変更
if 文などのコード生成
- 条件を条件付きジャンプ命令に変更
- 条件付き命令は前の演算から残るフラグを使ったり、0
との比較が多い
- 条件が合わない場合にジャンプすることが多い
- 例:
if (a>b)
→ a-b; jumpLE0
- ジャンプの行き先がまだ分からない場合が多い
関数呼び出しのコード生成
- 呼び出し側と関数側に特別なコードが必要
- 機械・OS・言語特有の関数呼び出しスタックの構成を考慮する必要がある
- 関数呼び出しスタックの内容 (関数フレーム):
- 戻り番地 (関数後どこに戻るか)
- 引数、戻り値
- 前の関数フレームのベースポインタ
- 使われるレジスターの値を退避する一時変数
- ローカル変数
最適化の目的
- プログラムの実行速度の向上
- コードの量の削減
- 次のことを保つ:
- プログラムの意味
- コンパイルの速度
- デバグのしやすさ
最適化の手法
- 制御フロー解析 (control flow analysis)
- プログラムを順番だけで実行される部分
(基本ブロック) に切り分ける
- 基本ブロックをノードに制御フローのグラフを作る
- データフロー解析 (data flow analysis)
- 制御フロー解析の結果、どこの変数の代入がどこの変数の使用に影響を及ぼせるかを分析
最適化の手段 (1)
複数の手法を繰り返し組み合わせて少しづつ最適化
- 静式評価 (constant folding, 定数たたみこみ)
- 定数伝播 (constant propagation)
- 共通の式の繰り返しからの追い出し
- 無用命令の削除 (dead code elimination)
- 命令をループの外へ移動
- 演算の変更 (例:
x*2
→ x+x
もしくは x<<1
)
最適化の手段 (2)
この最適化はコードの量を増やすが、速くする
- 関数呼び出しの展開
- 繰り返しの展開
- 少定数の繰り返しの展開
(コードが小さくなる可能性もある)
- 一部展開
(20回の繰り返しを4部展開して5回繰り返す)
最適化の手段 (3)
この手段は機械に強く依存する
- 命令の入れ替え
(命令によってかかる時間が違う、大きさが違う)
- 命令の順番変更 (LOAD
の後すぐ使うのではないなど)