PythonでLLVM IRを出力してみた

Pythonにはllvmliteというパッケージがあり、これが非常に便利だった！
なんてったってLLVM IRが楽に出力できる！LLVMの最初の一歩にはすごくいいと思う。

そんなわけで「llvmliteのインストール」〜「ちょっと動かしてみる」までやってみる！

# 環境

Windows7にVirtualBoxでUbuntuを仮想化してインストールした。
基本的にUbuntu派生のディストリなら動くと思う。実際、自分はLinuxMint 17.3(Cinnamon 64bit)でも同様の手順でインストールしたが、ちゃんと動いている。

* ホスト
    * Windows7 64bit
* 仮想化ソフトウェア
    * VirtualBox
* ゲストOS
    * Ubuntu 14.04.3 64bit
* プロセッサ数
    * 1
* メモリ
    * 2GB※

※2GBほどないと、LLVMのmakeがメモリ不足でこけます。

# とりあえずapt-getで必要なものを準備

LLVMをビルドする準備と、Python3の開発環境を用意。

```
$ sudo apt-get install build-essential cmake python3-dev python3-pip python3-setuptools zlib1g-dev libtool
```

# LLVMをビルド

## サイトからダウンロード

llvmliteはLLVM3.6が必要。apt-getで取得してもいいけど、自分でちゃんとインストールディレクトリとか管理したいので、自前ビルドする。

```
$ wget http://llvm.org/releases/3.6.2/llvm-3.6.2.src.tar.xz
```

## ビルド

cmakeを使うのが自分の好み。LLVMをビルドするのは、ちょっと時間がかかるので気長に待つ。（並列ビルドするならmake -j4とか使って）

```
$ tar xvf llvm-3.6.2.src.tar.xz
$ cd llvm-3.6.2.src
$ mkdir build
$ cd build
$ cmake -D CMAKE_INSTALL_PREFIX=/usr/local/llvm-3.6 ..
$ make
$ sudo make install
```

## bashrcの設定

LLVMにパスを通してやる

```bash
export LLVM_HOME=/usr/local/llvm-3.6
export PATH=${LLVM_HOME}/bin:${PATH}
export LD_LIBRARY_PATH=${LLVM_HOME}/lib:${LD_LIBRARY_PATH}
export LLVM_CONFIG=${LLVM_HOME}/bin/llvm-config

# 念の為Cのincludeパスも設定
export C_INCLUDE_PATH=${LLVM_HOME}/include:${C_INCLUDE_PATH}
export CPLUS_INCLUDE_PATH=${LLVM_HOME}/include:${CPLUS_INCLUDE_PATH}
```

## チェック

```
$ llvm-config --version
3.6.2
```


# llvmliteをインストール

## Pythonのライブラリを準備

たぶん必要そうなものをインストール。不要なものもあるかも。
Pythonのライブラリはapt-getだったりpipだったり、いろんなインストール方法があります。scipyとmatplotlibだけpipですんなりいけなかったので、apt-getでインストールしました。

```
$ sudo -E pip3 install numpy
$ sudo apt-get install python3-scipy python3-matplotlib
$ sudo -E pip3 install pandas statsmodels sympy
```

※環境変数を引き継ぐため、sudoに-Eを付けてます。

## llvmliteをインストール

手っ取り早く、llvmliteはpipでインストール

```
$ sudo -E pip3 install llvmlite
```

これで完了！

# お試し

## お試し実行

では、お試し実行です！
以下のPythonファイルを実行できれば、ちゃんと動いてます！

下記は私が適当に作ったサンプルです。

### サンプル

```py
from ctypes import CFUNCTYPE, c_int

import llvmlite.ir as ll
import llvmlite.binding as llvm

"""
これをLLVM IRで作る。
def func(x, y):
    a = x
    b = y
    c = 1000
    d = a
    return a + b + c + d
"""

# 初期化
llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

i32 = ll.IntType(32)

# int func(int, int)
fnty = ll.FunctionType(i32, [i32, i32])
module = ll.Module()
func = ll.Function(module, fnty, name="func")
bb_entry = func.append_basic_block()

builder = ll.IRBuilder()
builder.position_at_end(bb_entry)

# 引数のx, y
x, y = func.args

# 変数a,b,c,dを定義
ptr_a = builder.alloca(i32)
ptr_b = builder.alloca(i32)
ptr_c = builder.alloca(i32)
ptr_d = builder.alloca(i32)

# store
builder.store(x, ptr_a)
builder.store(y, ptr_b)
builder.store(ll.Constant(i32, 1000), ptr_c)

# load
a = builder.load(ptr_a)
b = builder.load(ptr_b)
c = builder.load(ptr_c)

# またstore
builder.store(a, ptr_d)

# 加算して、Returnする
ret1 = builder.add(a, b, name="res")
ret2 = builder.add(ret1, c, name="res2")
ret3 = builder.add(ret2, builder.load(ptr_d), name="res3")
builder.ret(ret3)

llvm_ir = str(module)
llvm_ir_parsed = llvm.parse_assembly(llvm_ir)

print("== LLVM IR ====================")
print(llvm_ir_parsed)

# pass
pmb = llvm.create_pass_manager_builder()
pmb.opt_level = 1
pm = llvm.create_module_pass_manager()
pmb.populate(pm)

pm.run(llvm_ir_parsed)

print("== LLVM IR(opt) ===============")
print(llvm_ir_parsed)

target_machine = llvm.Target.from_default_triple().create_target_machine()

print("== Result =====================")
with llvm.create_mcjit_compiler(llvm_ir_parsed, target_machine) as ee:
    ee.finalize_object()
    cfptr = ee.get_function_address("func")

    cfunc = CFUNCTYPE(c_int, c_int, c_int)(cfptr)
    res = cfunc(100, 2)

    print("res: " + str(res))
```

### 実行結果

```
== LLVM IR ====================
; ModuleID = '<string>'
target triple = "unknown-unknown-unknown"

define i32 @func(i32 %.1, i32 %.2) {
.4:
  %.5 = alloca i32
  %.6 = alloca i32
  %.7 = alloca i32
  %.8 = alloca i32
  store i32 %.1, i32* %.5
  store i32 %.2, i32* %.6
  store i32 1000, i32* %.7
  %.12 = load i32* %.5
  %.13 = load i32* %.6
  %.14 = load i32* %.7
  store i32 %.12, i32* %.8
  %res = add i32 %.12, %.13
  %res2 = add i32 %res, %.14
  %.16 = load i32* %.8
  %res3 = add i32 %res2, %.16
  ret i32 %res3
}

== LLVM IR(opt) ===============
; ModuleID = '<string>'
target triple = "unknown-unknown-unknown"

; Function Attrs: nounwind readnone
define i32 @func(i32 %.1, i32 %.2) #0 {
.4:
  %factor = shl i32 %.1, 1
  %res2 = add i32 %.2, 1000
  %res3 = add i32 %res2, %factor
  ret i32 %res3
}

attributes #0 = { nounwind readnone }

== Result =====================
res: 1202
```

LLVM IR(opt)のところを見ると。無駄にstoreとかしている部分がなくなっていることがわかる。

## example
本家のGithubにexampleもあるので、参考にするといい。
https://github.com/numba/llvmlite

# その他、参考

LLVM本家にはkaleidoscopeというTutorialがある。それをllvmliteで実装したものが以下にあって、すごく参考になるので紹介。

https://github.com/eliben/pykaleidoscope