项目作者: 0918nobita

项目描述 :
LLVM IR勉強用
高级语言: C++
项目地址: git://github.com/0918nobita/llvm-practice.git
创建时间: 2018-06-28T04:55:03Z
项目社区:https://github.com/0918nobita/llvm-practice

开源协议:

下载


LLVM IRを気軽に勉強してみる

全力で要約すると

想定する読者

このC言語プログラムを理解できる人

  1. #include<stdio.h>
  2. int main() {
  3. printf("Hello, world");
  4. return 0;
  5. }

想定する開発環境

  • llvm, clang がインストール済みの Linux

単純なプログラムをLLVM IRに変換してみる

  1. int main() {
  2. return 334;
  3. }

↑これを、clang -S -emit-llvm -O0 test.c コマンドで LLVM IR に変換すると…

↓こんなファイルが出力されます。要らなそうな部分は消しました。

  1. define i32 @main() #0 {
  2. ret i32 334
  3. }

↓容易に想像できることをまとめてみました。

LLVM IR なにそれ
define 関数定義
i32 32bit 整数型 (この関数の返り値の型)
@main 関数名「main」
#0 謎(1) なんか Attribute ってやつらしい
{} 関数定義の内容を囲むブロック的なあれ
ret i32 810 たぶん return 810; と対応してる

わからないことには↑の「謎(1)」のようにチェックをつけていきます。
まぁ、そのうちわかるんでしょう、たぶん。

変数を使ったプログラムを変換してみる

  1. int main() {
  2. int a = 2;
  3. return a;
  4. }
  1. define i32 @main() #0 {
  2. %1 = alloca i32, align 4
  3. %2 = alloca i32, align 4
  4. store i32 0, i32* %1, align 4
  5. store i32 2, i32* %2, align 4
  6. %3 = load i32, i32* %2, align 4
  7. ret i32 %3
  8. }

%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 つの i32 型データのために 2 箇所メモリ確保して、それぞれのアドレスを %1 , %2 に格納する
  2. %1 に値 0 を、%2 に値 2 を格納する
  3. %2 の値を %3 に格納する
  4. %3 の値を終了コードとして返す

要は『メモリに 02 を書き込んで、2 の方を終了コードとする』ってことです。
ん? 0 をメモリに書き込ん消してみますだのはなぜ?その処理、いらなくね?

思い切って、要らなそうな行を消してみます。

  1. define i32 @main() #0 {
  2. %1 = alloca i32, align 4
  3. store i32 2, i32* %1, align 4
  4. %2 = load i32, i32* %1, align 4
  5. ret i32 %2
  6. }

↓実行してみました

  1. $ lli test_re.ll; echo $?
  2. 2

やっぱりね!初めて自分が書いたLLVM IRを動かすことができました!はい拍手!!

でも、なんで 0 を書き込む処理が含まれていたのでしょうか…?
それは、まだ初期化されていない変数にはでたらめな値が入っていて、ここでは a にまず 0 が代入されるからです。

でも、最適化処理を行うと、 0 を書き込む処理はなくなります。試してみます。

  1. $ clang -S -emit-llvm -O1 test2.c
  2. $ lli test2.ll; echo $?
  3. 2
  1. int main() {
  2. int a = 2;
  3. int b = 3;
  4. return a + b;
  5. }
  1. define i32 @main() #0 {
  2. %1 = alloca i32, align 4
  3. %2 = alloca i32, align 4
  4. %3 = alloca i32, align 4
  5. store i32 0, i32* %1, align 4
  6. store i32 2, i32* %2, align 4
  7. store i32 3, i32* %3, align 4
  8. %4 = load i32, i32* %2, align 4
  9. %5 = load i32, i32* %3, align 4
  10. %6 = add nsw i32 %4, %5
  11. ret i32 %6
  12. }
  1. int main() {
  2. int a = 2;
  3. int b = 3;
  4. return a + b;
  5. }
  1. ```
  2. ## 関数定義を含むプログラムを変換してみる
  3. ```c:test5.c
  4. int main() {
  5. return add(1, 2);
  6. }
  7. int add(int x, int y) {
  8. return x + y;
  9. }

text:test5.ll