clang -O2(またはオンラインデモ)を使用して次のコードスニペットをコンパイルした後:
#include <stdio.h>
#include <stdlib.h>
int flop(int x);
int flip(int x) {
if (x == 0) return 1;
return (x+1)*flop(x-1);
}
int flop(int x) {
if (x == 0) return 1;
return (x+0)*flip(x-1);
}
int main(int argc, char **argv) {
printf("%d\n", flip(atoi(argv[1])));
}
llvmアセンブリの次のスニペットを次の場所で取得していflip
ます:
bb1.i: ; preds = %bb1
%4 = add nsw i32 %x, -2 ; <i32> [#uses=1]
%5 = tail call i32 @flip(i32 %4) nounwind ; <i32> [#uses=1]
%6 = mul nsw i32 %5, %2 ; <i32> [#uses=1]
br label %flop.exit
これは、現在のスタックをドロップすることを意味すると思いましたtail call
(つまり、リターンは上位フレームに戻るので、次の命令はret %5
)である必要がありますが、このコードによれば、それはそれを実行mul
します。そして、ネイティブアセンブリではcall
、テールの最適化なしで単純です(llcに適切なフラグがある場合でも)
誰かがclangがそのようなコードを生成する理由を説明できますか?
同様に、llvmが、nextがprevの結果を使用し、後で適切な最適化を実行するか、末尾呼び出し命令と同等のネイティブを生成するtail call
ことを単純にチェックできる場合、なぜllvmが持つのか理解できません。ret
call