LLVMには phi 命令があり、非常に奇妙な説明があります。
「phi」命令は、関数を表すSSAグラフのφノードを実装するために使用されます。
通常、分岐の実装に使用されます。正しく理解できたら、依存関係分析を可能にする必要があり、場合によっては不必要なロードを回避するのに役立つ可能性があります。しかし、それが何をするのかを正確に理解することはまだ困難です。
Kaleidoscope 例 は、if
の場合にかなりうまく説明します。ただし、&&
や||
などの論理演算を実装する方法は明確ではありません。 online llvm コンパイラに次を入力すると:
void main1(bool r, bool y) {
bool l = y || r;
}
最後のいくつかの行は私を完全に混乱させます:
; <label>:10 ; preds = %7, %0
%11 = phi i1 [ true, %0 ], [ %9, %7 ]
%12 = zext i1 %11 to i8
Phiノードが使用可能な結果を生成するように見えます。そして、phiノードは値がどのパスから来るのかを定義するだけだという印象を受けました。
誰かがPhiノードとは何か、それを使って||
を実装する方法を説明できますか?
Phiノードは、現在のブロックの前身に応じて値を選択するために使用される命令です( here を参照して完全な階層を確認します。これは、値としても使用され、クラスの1つです)から継承)。
Phiノードは、LLVMコードのSSA(静的単一割り当て)スタイルの構造のために必要です-たとえば、次のC++関数
void m(bool r, bool y){
bool l = y || r ;
}
次のIRに変換されます:(clang -c -emit-llvm file.c -o out.bc
で作成され、llvm-dis
で表示されます)
define void @_Z1mbb(i1 zeroext %r, i1 zeroext %y) nounwind {
entry:
%r.addr = alloca i8, align 1
%y.addr = alloca i8, align 1
%l = alloca i8, align 1
%frombool = zext i1 %r to i8
store i8 %frombool, i8* %r.addr, align 1
%frombool1 = zext i1 %y to i8
store i8 %frombool1, i8* %y.addr, align 1
%0 = load i8* %y.addr, align 1
%tobool = trunc i8 %0 to i1
br i1 %tobool, label %lor.end, label %lor.rhs
lor.rhs: ; preds = %entry
%1 = load i8* %r.addr, align 1
%tobool2 = trunc i8 %1 to i1
br label %lor.end
lor.end: ; preds = %lor.rhs, %entry
%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
%frombool3 = zext i1 %2 to i8
store i8 %frombool3, i8* %l, align 1
ret void
}
ここで何が起こるのでしょうか?変数bool l
が0または1の可能性があるC++コードとは異なり、LLVM IRで定義する必要がありますonce。したがって、%tobool
が真であるかどうかを確認し、lor.end
またはlor.rhs
にジャンプします。
lor.end
には、||の値が最終的にありますオペレーター。エントリブロックから到着した場合-それは本当です。それ以外の場合は、%tobool2
の値と等しくなります。これは、次のIR行から得られるものです。
%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
Phiを使用する必要はまったくありません。一時変数の束を作成するだけです。 LLVM最適化パスは一時変数の最適化を処理し、そのためにphiノードを自動的に使用します。
たとえば、これを行いたい場合:
x = 4;
if (something) x = x + 2;
print(x);
そのためにphiノードを(擬似コードで)使用できます:
ただし、phiノードなしで(擬似コードで)実行できます。
Llvmで最適化パスを実行すると、この2番目のコードは最初のコードに最適化されます。