最も単純なCコードを変換します
#include <stdio.h>
int main()
{
return 0;
}
lLVM IRに
clang -emit-llvm -S hello.c
生成されるIRは次のとおりです。
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
ret i32 0
}
しかし、私はこのIRを理解していません。 (LLVM docは役立ちますが、初心者にはそれほど役立ちません)
%1 = alloca i32, align 4
があるのですか?元のコードでは何に対応していますか?store i32 0, i32* %1
に対する同じ質問 define i32 @main() #0
これは、32ビット整数を返すmain
という関数を定義します。 #0
は、#0
という名前の属性を関数に使用することを意味します。たとえば、IRにattributes #0 = { alwaysinline alignstack=4 }
のようなものがあり、これらの属性はmain
に適用されます。
%1 = alloca i32, align 4
これにより、スタックに32ビット整数が割り当てられます。 %1
は、スタック上のこの場所へのポインターの名前です。 align 4
は、アドレスが4の倍数になることを保証します
store i32 0, i32* %1
これは、%1
が指す32ビット整数を32ビット値0に設定します。これは、C++で*x = 1
と言うようなものです
ret i32 0
これは、32ビットの戻り値が0の関数から返されます。
main
にローカル変数がないことを考えると、割り当ては奇妙です。 LLVMはBasicBlock
を使用して命令のグループを表し、基本ブロックには出口点と命令のリストがあります。私の推測では、コンパイラーはreturn
を基本ブロックの出口として使用することを決定し、少なくとも1つの命令をブロックに入れることを選択しました。割り当ては基本的に何もしません。
%n
は、ターゲットマシンのコードを生成するときに実際のレジスタに解決される仮想レジスタです。
i32
はタイプ情報用です。元のコードでは、コンパイラは32ビット整数であるint
でした。
alloca
は、スタック上のスペースを割り当てるためのものです。この例ではi32
(32ビット整数)なので、戻り値として0をロードできます。 align 4
は、この割り当てに4バイト境界整列を与えます。つまり、スタックポインターは4バイト境界整列アドレスになります。
これは最も効率的な表現ではありませんが、IRの目的ではありません。 IRはさまざまなアーキテクチャに移植できる必要があります。その後、効率的なマシンコードを生成するためにバックエンドに任されます。
なぜalloca
とstore
があるのかは、これがmain
関数であることと関係があります。この関数を別の場所で呼び出した場合、IRには期待どおりret
が含まれます。 main用に生成されたアセンブリを調べると、スタックベースポインターに関連しているように見えますが、なぜそこにあるのか完全に理解できません。 C標準を抜く時間だと思います。
更新:C標準には何も見つかりませんでしたが、clangはすべてのメイン関数に対してこれを行うようです。私はそれを追跡するのに十分なほどclangコードベースを知りません。
更新:以下のBill Lynchのコメントを参照してください。これらの指示があります:
可能な暗黙の
return 0
主な機能は
変数は通常、デバッグ上の理由から、最適化されていないビルドのスタックに置かれます。実際のレジスタを使用する最適化されたビルドでは、関数が終了する前に値が消えることがあります。
移植性に関するコメントは正確ではありません。このIRが「opt」を介して渡された場合、スタックストアが削除されます。