LoginSignup
37
28

More than 5 years have passed since last update.

PythonでLLVM IRを出力してみた

Last updated at Posted at 2016-01-23

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で実装したものが以下にあって、すごく参考になるので紹介。

37
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
28