Compiler Construction Lecture 2/1

Compiler Construction Lecture 2/1

先週の復習

先週は、以下のことを勉強した。

   484	reverse(t1)
   485	int t1;
   486	{int t2,t3;
   487		t2=t1;
   488		while(type!=t1)
   489		{	t3=heap[type+1];
   490			heap[type+1]=t2;
   491			t2=type;
   492			type=t3;
   493		}
   494		type=t2;
   495	}
このフローグラフを考えてみよう。

flow-chart と違い、基本ブロックは最適化の単位となるので、可 能な限り一つにまとめなければならない。結果は以下のようになる。




大域的最適化 Global Optimization

peep holeや基本ブロックの中だけの解析には限界がある。特に、 レジスタの有効利用をおこなうためには、使われない変数を探しだ し、それに割り当てたレジスタを再利用することが重要である。ま た、直前の基本ブロックで再利用出来る値があれば、それを使うこ とが望ましい。そのためには、フローグラフ上で解析を行うのが良 い。

これらの解析は大域的データフロー解析(Global dataflow analsys) と呼ばれる。まず、基本ブロックの変数の代入に番号をつける。こ の代入によって定義された値(gen)がある。一方、代入された変数 の前の値は死んでしまった(kill)。そして、基本ブロックには、入 力(in)と出力(out) がある。gen,kill,in,out は変数の代入の番号 の集合である。基本ブロックSに対して、これらの間にフロー方程 式と呼ばれる関係がある。

out[S] = gen[S]∪(in[S] - kill[S]) 
この方程式を利用して、あるブロックで他のどのブロックの定数が生きている かどうかを調べよう。(1)
in[B] = {}          B は開始ブロック
in[S] = ∩ out[P]   P はSに先行するブロック
out[S] = gen[S]∪(in[S] - kill[S]) 
この方程式を繰り返してフローグラフに適用すると、いつかは変化 がなくなる。最後に得られたin[S]により、この時点での利用可能 な式がわかる。これらの演算はビット演算(and, or)を使うことが でき高速に計算することができる。

逆にoutからinを計算すると、その時点で生きている変数を調べる ことができる。(2)ここでは、変数の使用よりも優先して確実に定義さ れる変数の集合をdef[B]、変数の定義よりも優先して使用される変 数の集合をuse[B]とする。defとする。

in[S] = use[S]∪(out[S] - def[S]) 
out[S] = ∪ in[B]    B は後続ブロック

問1

上のフローグラフについて、(1),(2) の 大域的データフロー解析をおこなってみよ。



レジスタ割り当て Register allocation

前の方法で生きている変数がわかると、それにレジスタを割り当て ることになる。同時に生きている変数には同じレジスタを割り当て てはいけない。

このためには、変数を節点とするグラフを考える。同時に生きてい る変数を線で結ぶ。隣同士が同じ色にならないように、グラフを塗 り分けることができれば、その色がレジスタ割り当てを表している。

もちろん適当に塗り分けるのではなく、ループの中を優先するなど の努力が必要である。

問2

以下のフローグラフのレジスタ割り当てを考えてみよ。まず、手で 割り当てて見て、次に以下の手順で割当が正しいかどうかを確認せよ。
  1. フロー解析をおこなう
  2. グラフを作成する