coins memo no.5

前回の記事はこちら

coins関係はこちら


で,具体的に最適化器をつくる.

今回はビットベクタ法を用いたデータフロー解析を行って定数伝搬の最適化を行う.

(ビットベクタ法に関する論文はコチラ : Uday.P, et.al., A generalized theory of bit vector data flow analysis, ACM Tran. on Prog. Lang. and Sys. ,16 ,5 ,1472 (1994))

で,実装の参考にこちらの"データフロー解析を用いた共通部分式除去"のソースコードを,

coins組み込みにはこちらの"低水準中間表現(LIR)での最適化"を参考にした.

また,coinsの低水準中間表現方式(Low Intermediate Representaion, LIR)についてはこちら.このリンク先の図を見ながらLIRの内部表現を学習すると理解が早い.


で,まずは全体の流れ.

4つ組(3番地形式)の制御フローグラフのデータフロー解析をおこない,到達定義を計算して定数伝搬の最適化を行う.

到達定義は教科書等に出ているその定義をもとに計算する.

それ以外の実装の基本となるアルゴリズム

  • ビットベクタ法

要素iを持つ有限領域上の集合Sはビットベクタで表現できる.
ビットベクタを用いると各種演算が集合演算になる為,実行が高速である.
具体的にはcoinsでは"coins.ssa.BitVector"クラスが利用可能である.

  • 基本ブロック

基本ブロックにinstructionをまとめることによりgenkillinoutの数を減らせる.
coinsでは基本ブロックを"coins.backend.dfg.BasicBlk"クラス,基本ブロック内の文を"coins.backend.lir.LirNode"で表現する.
その中身はリスト構造である.
他にもLirNodeへのリンクやリストを表す"coins.backend.uti.BiLink", "coins.backend.util.BiLink"などがある.
JavaScriptのDOMや,S式の操作を考えると理解しやすい.

  • 節の順序付け

制御フローグラフをトポロジカル整列することによりデータフロー解析を1パスで完了する.
LIR内部表現の仕様BasicBlkクラスのオブジェクトはpredecessorsuccessorのリストを返すメソッドを持つので(predList()とsuccList())
多分おk.

  • 使用定義(UD)チェイン

到達定義の情報は,変数の各使用に到達する定義リスト"使用定義チェイン"を用いて保持できる.
これを用いる事で効率的に最適化アルゴリズムを実装できる.

UDチェインはいずれかのout集合が変更されると,すべての4つ組を再計算する必要が有る.
これを効率的に行うために,out集合が再計算されなければならない部分だけをワークリストに保持しておく.
実際はprocedure initの最後で,outの変化のチェックを行い,outの変化の有ったものをワークリストに入れておく.
procedure settleで,ワークリストの中身をprocedure propagateに渡してやり,何度もoutを再計算させ,
変化が無くなるまでprocedure propagate再帰させる.

となる.参考にしたprocedure doItの流れ.

だいたいこんな感じ.

0.ターゲットとなる関数functionと不変リストargs(実際ここでは使わない多分)を受け取る
1.kill,gen,in,outのビットベクタの配列を作成(すべての要素数のサイズで)
2.変数代入が生じる箇所のid(変数名)を作成し,idMapの数をカウント(expId)する.ここがgenkillの対象となる.
prcedure collectExpは,基本ブロックを引数に取り,基本ブロックのBiLinkを辿って各文をチェックする.
代入文,すなわちoperater SETのものにユニークなidを振る.
d : t <- b binop c
d : t <- M[b]
d : t <- f(a1, ... ,an)
3.kill,gen,in,outの各ビットベクタにexpIdのサイズで初期化
4.procedure init
4.1各基本ブロックのgenとkillを初期化(procedure initGenKill)
4.1.1procedure initGenKill
genkillを設定して,置き換えできそうなところをスタックに詰めておく.
4.2inからoutを計算
4.3outの変化したものをワークリストworklistに詰めておく.
5.procedure settle
ワークリストworklistにそってを用いてoutの再計算をする.
procedure propagate再帰関数になっているので,再計算前後のoutを比較してさらに伝播をおこなう
6.update()
実際に定数伝播の置換を行う.
7.f.flowGraph.touch()
変更の適用
8return
おわた

多分,こんなかんじか...うお,キツい.

続きはまた...