LLVM IR勉強用
このC言語プログラムを理解できる人
#include<stdio.h>
int main() {
printf("Hello, world");
return 0;
}
int main() {
return 334;
}
↑これを、clang -S -emit-llvm -O0 test.c
コマンドで LLVM IR に変換すると…
↓こんなファイルが出力されます。要らなそうな部分は消しました。
define i32 @main() #0 {
ret i32 334
}
↓容易に想像できることをまとめてみました。
LLVM IR | なにそれ |
---|---|
define |
関数定義 |
i32 |
32bit 整数型 (この関数の返り値の型) |
@main |
関数名「main」 |
#0 |
謎(1) なんか Attribute ってやつらしい |
{ … } |
関数定義の内容を囲むブロック的なあれ |
ret i32 810 |
たぶん return 810; と対応してる |
わからないことには↑の「謎(1)」のようにチェックをつけていきます。
まぁ、そのうちわかるんでしょう、たぶん。
int main() {
int a = 2;
return a;
}
define i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 2, i32* %2, align 4
%3 = load i32, i32* %2, align 4
ret i32 %3
}
%1
, %2
, %3
のような、%
から始まるものはレジスタを示します。
以下で紹介する alloca
, store
, load
はメモリアクセス演算子です。
LLVM IR | なにそれ |
---|---|
%1 = alloca i32, align 4 |
スタックメモリの確保 ( アドレスが %1 に格納される ) |
store i32 0, i32* %1, align 4 |
第 2 引数に渡したポインタ %1 が指すメモリアドレスに、第 1 引数で指定した値 0 を格納する |
%3 = load i32, i32* %2, align 4 |
第 2 引数に渡したポインタ %2 が指すメモリアドレスから、値を取り出して %3 に格納する |
やっとわかってきました。main
関数の処理の流れを整理します。
%1
, %2
に格納する%1
に値 0
を、%2
に値 2
を格納する%2
の値を %3
に格納する%3
の値を終了コードとして返す要は『メモリに 0
と 2
を書き込んで、2
の方を終了コードとする』ってことです。
ん? 0
をメモリに書き込ん消してみますだのはなぜ?その処理、いらなくね?
思い切って、要らなそうな行を消してみます。
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 2, i32* %1, align 4
%2 = load i32, i32* %1, align 4
ret i32 %2
}
↓実行してみました
$ lli test_re.ll; echo $?
2
やっぱりね!初めて自分が書いたLLVM IRを動かすことができました!はい拍手!!
でも、なんで 0
を書き込む処理が含まれていたのでしょうか…?
それは、まだ初期化されていない変数にはでたらめな値が入っていて、ここでは a にまず 0 が代入されるからです。
でも、最適化処理を行うと、 0
を書き込む処理はなくなります。試してみます。
$ clang -S -emit-llvm -O1 test2.c
$ lli test2.ll; echo $?
2
int main() {
int a = 2;
int b = 3;
return a + b;
}
define i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 2, i32* %2, align 4
store i32 3, i32* %3, align 4
%4 = load i32, i32* %2, align 4
%5 = load i32, i32* %3, align 4
%6 = add nsw i32 %4, %5
ret i32 %6
}
int main() {
int a = 2;
int b = 3;
return a + b;
}
```
## 関数定義を含むプログラムを変換してみる
```c:test5.c
int main() {
return add(1, 2);
}
int add(int x, int y) {
return x + y;
}
text:test5.ll