単子葉類プログラマーのメモ

プログラミング関連の自分用メモだけど他の人の役に立つかもしれないので公開しておく感じのブログ

LLVM言語 学習メモ (3) - phiによる条件分岐、ループの効率化

LLVMphi命令についてのメモ。

内容は、phi命令がなぜ必要なのかという説明、使い方、phi命令を使わない場合の条件分岐やループの例と使った場合の例。

LLVMのバージョンは10.0.0。

目次

なぜphi命令が必要なのか

LLVM静的単一代入(SSA)という方式を採用している。

そのため、一度識別子に値を割り当てたら、その後別の値を割り当て直すことはできない。

例えば以下のようなことができない。

%result = call i32 func1()
%result = call i32 func2()

以下のように書く必要がある。

%result1 = call i32 func1()
%result2 = call i32 func2()

これで何が困るかというと、例えば以下のように条件分岐によって違う値を識別子に割り当てたい場合に困る。

コンパイルするとmultiple definition of local value named 'result'というエラーになる。

これを避けるため、前回書いた条件分岐のコードではスタック領域を利用した。

前回書いたコードは以下の通り。

allocastoreloadを使っている箇所が、スタック領域の確保や読み書きを行っている部分。 スタック領域の変数%ptr_return_valueで値を使いまわしている。

この場合、その部分を削除してphi命令を使うと、スタック領域を使わずレジスタだけで処理が可能になる。

スタック領域よりレジスタを使うほうが実行速度の向上が見込める。また、LLVM IRのコード量も減らせる。

phi命令とは

先述の例のようにmultiple definition of local value named 'result'のエラーが発生する場合、”どこから分岐してきたか"によって割り当てる値を切り替えることができれば、エラーを直すことができる。

それをするための命令がphi

phi命令の説明はhttps://releases.llvm.org/10.0.0/docs/LangRef.html#phi-instructionにある。

構文は以下。

<result> = phi [fast-math-flags] <ty> [ <val0>, <label0>], ...

<ty>が戻り値の型。

[ <val0>, <label0>], ...が、分岐してくる前の位置とその場合の値のリスト。カンマ区切りで複数指定できる。

phi命令を使った場合の分岐

6行目から13行目に遷移してきた場合、つまりラベルthenのブロックから遷移してきた場合は%result1の値を%result3に割り当てる。

10行目から13行目に遷移してきた場合、つまりラベルelseのブロックから遷移してきた場合は%result2の値を%result3に割り当てる。

phi命令を使った場合のループ

値を割り当て直すことができないというのはソースコード上での話。

動作時、ループで戻ってきて値を割り当て直すことはできる。

phi命令を使った場合のFIzzBuzz

前回と違い、配列部分以外、allocastoreloadしていない。

optコマンド

ちなみに、理解するために今回は自分でコードを書いたがoptコマンドを使えば自動的に最適化できるので自分でphi命令を使わなくてもいい。

コマンドの実行例:opt -S -mem2reg -o (出力ファイル名).ll (入力ファイル名).ll

ただし、mem2regで最適化するためには、allocaを関数の先頭ブロックで行わなければならない。