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にパスを通してやる
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ファイルを実行できれば、ちゃんと動いてます!
下記は私が適当に作ったサンプルです。
サンプル
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で実装したものが以下にあって、すごく参考になるので紹介。