web-dev-qa-db-ja.com

最も単純なLLVM IRを理解する

最も単純な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. なぜ%1 = alloca i32, align 4があるのですか?元のコードでは何に対応していますか?
  2. store i32 0, i32* %1に対する同じ質問
  3. Allocaは(動的割り当てではなく)スタックへの割り当てを意味しますか?
  4. 「align 4」はどういう意味ですか?
44
zell
 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つの命令をブロックに入れることを選択しました。割り当ては基本的に何もしません。

41
Sean

%nは、ターゲットマシンのコードを生成するときに実際のレジスタに解決される仮想レジスタです。

i32はタイプ情報用です。元のコードでは、コンパイラは32ビット整数であるintでした。

allocaは、スタック上のスペースを割り当てるためのものです。この例ではi32(32ビット整数)なので、戻り値として0をロードできます。 align 4は、この割り当てに4バイト境界整列を与えます。つまり、スタックポインターは4バイト境界整列アドレスになります。

これは最も効率的な表現ではありませんが、IRの目的ではありません。 IRはさまざまなアーキテクチャに移植できる必要があります。その後、効率的なマシンコードを生成するためにバックエンドに任されます。

LLVM言語リファレンスマニュアル

なぜallocastoreがあるのか​​は、これがmain関数であることと関係があります。この関数を別の場所で呼び出した場合、IRには期待どおりretが含まれます。 main用に生成されたアセンブリを調べると、スタックベースポインターに関連しているように見えますが、なぜそこにあるのか完全に理解できません。 C標準を抜く時間だと思います。

更新:C標準には何も見つかりませんでしたが、clangはすべてのメイン関数に対してこれを行うようです。私はそれを追跡するのに十分なほどclangコードベースを知りません。

更新:以下のBill Lynchのコメントを参照してください。これらの指示があります:

可能な暗黙のreturn 0主な機能は

14
DrYap

変数は通常、デバッグ上の理由から、最適化されていないビルドのスタックに置かれます。実際のレジスタを使用する最適化されたビルドでは、関数が終了する前に値が消えることがあります。

移植性に関するコメントは正確ではありません。このIRが「opt」を介して渡された場合、スタックストアが削除されます。

2
Colin LeMahieu