SlideShare ist ein Scribd-Unternehmen logo
1 von 56
Downloaden Sie, um offline zu lesen
きつねさんでもわかる
LLVM読書会 第2回
2013/7/6
cuzic
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
自己紹介
最近のできごと
Ruby勉強会で TRICK に関する発表をしました。
TRICK: 超絶技巧Ruby意味不明コンテスト
IOCCC の Ruby版
やっつけで資料を作ったわりにはみなさんから
好評だったようでよかったです
いろんな活動を始めました
山登り
クーリエジャポン朝食会
次回の勉強会
次回の amagasakirb は
8月10日(土)で考えています。
Land of Lisp の読書会の予定です。
11
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
今日の構成
3本立てです。
LLVM 概要と Ruby-LLVM
LLVM がどんなものかについて簡単に紹介
LLVM のコードを生成する Ruby-LLVM ライブラリを紹介
Hello, World
階乗を計算
Parslet と Ruby-LLVM の夢の競演
PEG パーサ生成器 Parslet を紹介
Parslet で電卓プログラムを作成
四則演算を正しく処理する
LLVM の Pass について
Pass の作り方
各種 Pass の概要を紹介
主要な最適化 Pass について紹介
22
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM概要と Ruby-LLVM
33
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM とは
LLVM とは
コンパイラを作成するための共通基盤
BSDライセンス で C++ で開発
多くのサブプロジェクトから構成される
LLVM Core、Clang、 etc
LLVM Core とは
言語やアーキテクチャから独立した中間表現 LLVM IR を規定
新言語の開発で字句解析や構文解析だけを作成し、他は流用できる
最適化や新たなアーキテクチャ対応の試作なども可能
さまざまな最適化、多くのアーキテクチャへのコード生成に対応
LLVM IR のレベルで最適化するため、言語やアーキテクチャから独立
C++ 等の言語へのフロントエンド実装 Clang が有名
LLVM をコンパイラ基盤ではなくコンパイラとして利用可能
44
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
環境構築
Debian 7.0 (Wheezy) で環境を構築
aptitude で簡単にインストール
単に、 aptitude install llvm とすると 3.0 になった。
llvm-3.1 では、 -3.1 という suffix に要注意
例: llvm-config ではなく llvm-config-3.1
PATH を通すか alias が必要
55
sudo aptitude install llvm-3.1 llvm-3.1-dev llvm-3.1-doc
llvm-3.1-examples llvm-3.1-runtime llvm-3.1-source
$ export PATH=/usr/lib/llvm-3.1/bin:$PATH
### LLVM の最新版を使いたいなら
$ sudo cat >> /etc/apt/sources.list
deb http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main
deb-src http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main
^D
$ sudo aptitude install llvm-3.3 …
sudo aptitude install llvm-3.1 llvm-3.1-dev llvm-3.1-doc
llvm-3.1-examples llvm-3.1-runtime llvm-3.1-source
$ export PATH=/usr/lib/llvm-3.1/bin:$PATH
### LLVM の最新版を使いたいなら
$ sudo cat >> /etc/apt/sources.list
deb http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main
deb-src http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main
^D
$ sudo aptitude install llvm-3.3 …
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM
Ruby で LLVM の実行コードの生成を行うライブラリ。
できること
LLVM IR のコードの出力
LLVM JIT Compiler を使えば、その場で実行もできる
オレオレ言語の開発がカンタン
パーサ: treetop、citrus、rsec、parslet
コードジェネレータ: Ruby-LLVM
インストール方法
66
### Ruby-LLVM のインストール
$ export PATH=/usr/lib/llvm-3.1/bin:$PATH
$ gem install ruby-llvm
### Ruby-LLVM のインストール
$ export PATH=/usr/lib/llvm-3.1/bin:$PATH
$ gem install ruby-llvm
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
(復習) 簡単な LLVM IR の例
http://www.aosabook.org/en/llvm.html
77
define i32 @add1(i32 %a, i32 %b) {
entry:
%tmp1 = add i32 %a, %b
ret i32 %tmp1
}
define i32 @add2(i32 %a, i32 %b) {
entry:
%tmp1 = icmp eq i32 %a, 0
br i1 %tmp1, label %done, label %recurse
recurse:
%tmp2 = sub i32 %a, 1
%tmp3 = add i32 %b, 1
%tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
ret i32 %tmp4
done:
ret i32 %b
}
define i32 @add1(i32 %a, i32 %b) {
entry:
%tmp1 = add i32 %a, %b
ret i32 %tmp1
}
define i32 @add2(i32 %a, i32 %b) {
entry:
%tmp1 = icmp eq i32 %a, 0
br i1 %tmp1, label %done, label %recurse
recurse:
%tmp2 = sub i32 %a, 1
%tmp3 = add i32 %b, 1
%tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
ret i32 %tmp4
done:
ret i32 %b
}
unsigned add1(unsigned a, unsigned b) {
return a+b;
}
// not efficient way to add two numbers.
unsigned add2(unsigned a, unsigned b) {
if (a == 0) return b;
return add2(a-1, b+1);
}
unsigned add1(unsigned a, unsigned b) {
return a+b;
}
// not efficient way to add two numbers.
unsigned add2(unsigned a, unsigned b) {
if (a == 0) return b;
return add2(a-1, b+1);
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
(復習) Module, Function, Basic Block, Instruction 88
define i32 @add1(i32 %a, i32 %b) {
entry:
%tmp1 = add i32 %a, %b
ret i32 %tmp1
}
define i32 @add2(i32 %a, i32 %b) {
entry:
%tmp1 = icmp eq i32 %a, 0
br i1 %tmp1, label %done, label %recurse
recurse:
%tmp2 = sub i32 %a, 1
%tmp3 = add i32 %b, 1
%tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
ret i32 %tmp4
done:
ret i32 %b
}
define i32 @add1(i32 %a, i32 %b) {
entry:
%tmp1 = add i32 %a, %b
ret i32 %tmp1
}
define i32 @add2(i32 %a, i32 %b) {
entry:
%tmp1 = icmp eq i32 %a, 0
br i1 %tmp1, label %done, label %recurse
recurse:
%tmp2 = sub i32 %a, 1
%tmp3 = add i32 %b, 1
%tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
ret i32 %tmp4
done:
ret i32 %b
}
ModuleFunction
Basic Block
Instruction
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
(復習) Module, Function, Basic Block, Instruction
1つのソースコードは LLVM IR では Module に対応する
Module は Function や GlobalVariable などで構成される
Function
いわゆる関数 or メソッド
引数があって、一連の処理を行って、返り値を返す
複数の Basic Block で構成される
Basic Block
1つの入り口と、1つの出口がある
終端命令の例: ret、br、switch
Basic Block の最後には 1つの終端命令がある
Basic Block を表す名前(ラベル)が付いている
その名前で、別の Basic Block にジャンプできる
複数の Instruction で構成される
Instruction
四則演算、ビット演算、型変換、条件分岐、メモリアクセスなど
処理の最小単位
99
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での LLVM IR の生成
LLVM::Module を作る
Module に グローバル変数、外部関数の宣言などを追加する
Module に Function を追加する
Module#functions.add メソッドを使う
追加する Function を作る
プロトタイプを書く
Function に BasicBlock を追加する
Function#basic_blocks.append メソッドを使う
追加する BasicBlock を build する
Builder を使って、各種の Instruction を追加する
Instruction は四則演算とか、関数呼び出しとか return とか
環境を指定して、JIT で実行することもできる
LLVM.init_x86 ; おまじない
JITCompiler.new(m).run_function(func, args)
引数 args で モジュール m の関数 func を実行する。
1010
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM で Hello, World
Hello, World を出力する例を Ruby-LLVM で再実装する。
http://llvm.org/docs/LangRef.html#module-structure
1111
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world¥0A¥00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8 *...
%cast210 = getelementptr [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!1 = metadata !{i32 42}
!foo = !{!1, null}
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world¥0A¥00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8 *...
%cast210 = getelementptr [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!1 = metadata !{i32 42}
!foo = !{!1, null}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World 実装例 1212
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.string(str)
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
m.verify
m.dump
puts "-----------------------------------------------------"
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.string(str)
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
m.verify
m.dump
puts "-----------------------------------------------------"
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
デモ
1313
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (1)
おまじない
変数 m が新しく生成する LLVM の Module
1414
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (2)
グローバル変数を hello Module に追加
LLVM で文字列リテラルは、
LLVM.Array(LLVM::Int8, str.size + 1)
の型になる。
m.globals.add でグローバル変数を追加できる。
グローバル変数の名前は ".str"
さらに ".str" を "Hello, World!" で初期化する。
1515
# generate global string "Hello, World!"
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.String(str)
# generate global string "Hello, World!"
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.String(str)
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (3)
外部で定義された関数 puts を宣言する。
m.functions
LLVM::Module::FunctionCollection オブジェクト
FunctionCollection#add メソッド
第1引数: 外部関数の名前
第2引数: LLVM 上の引数の型を表す配列
第3引数: LLVM 上の返り値の型
返り値: LLVM::Function オブジェクト
1616
# External Declaration of the `puts` function
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# External Declaration of the `puts` function
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
extern int puts(const char *s);extern int puts(const char *s);
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (4)
メイン関数の定義
function.basic_blocks :
LLVM::Function::BasicBlockCollection オブジェクト
BasicBlock#build :
LLVM::Builder オブジェクトを生成してブロックに渡す
builder.gep:
複雑なデータ構造での格納場所(アドレス)を取得する。
1717
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (5)
LLVM::Module#verify で Module が valid かを
検証できる。
LLVM::Module#dump で Module の IR を出力可能
標準エラー出力で出力される
1818
m.verify
m.dump
m.verify
m.dump
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (6)
LLVM.init_x86
出力ターゲットのアーキテクチャを指定する。
JITCompiler.new(m).run_function(main, args)
main 関数を実行する。
1919
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World 実装例(再掲) 2020
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.string(str)
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
m.verify
m.dump
puts "-----------------------------------------------------"
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.string(str)
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
m.verify
m.dump
puts "-----------------------------------------------------"
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World の出力
Ruby-LLVM で出力した LLVM IR
だいたい再現できている。
2121
; ModuleID = 'hello'
@.str = global [14 x i8] c"Hello, World!¥00"
declare i32 @puts(i8*)
define i32 @main() {
%1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
-----------------------------------------------------
Hello, World!
; ModuleID = 'hello'
@.str = global [14 x i8] c"Hello, World!¥00"
declare i32 @puts(i8*)
define i32 @main() {
%1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
-----------------------------------------------------
Hello, World!
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM での階乗計算
LLVM IR での階乗計算を例に条件分岐を説明する
br 命令で他の Basic Block に処理を遷移できる
phi 命令でどの Basic Block 経由かで値を変えられる
2222
; ModuleID = 'Factorial'
define i32 @fac(i32 %n) {
entry:
%test = icmp eq i32 %n, 1
br i1 %test, label %result, label %recur
recur: ; preds = %entry
%n-1 = sub i32 %n, 1
%"fac(n-1)" = call i32 @fac(i32 %n-1)
%"n*fac(n-1)" = mul i32 %n, %"fac(n-1)"
br label %result
result: ; preds = %recur, %entry
%fac = phi i32 [ 1, %entry ], [ %"n*fac(n-1)", %recur ]
ret i32 %fac
}
; ModuleID = 'Factorial'
define i32 @fac(i32 %n) {
entry:
%test = icmp eq i32 %n, 1
br i1 %test, label %result, label %recur
recur: ; preds = %entry
%n-1 = sub i32 %n, 1
%"fac(n-1)" = call i32 @fac(i32 %n-1)
%"n*fac(n-1)" = mul i32 %n, %"fac(n-1)"
br label %result
result: ; preds = %recur, %entry
%fac = phi i32 [ 1, %entry ], [ %"n*fac(n-1)", %recur ]
ret i32 %fac
}
int factorial(int n) {
if (n == 0) return 1;
return n*factorial(n-1);
}
int factorial(int n) {
if (n == 0) return 1;
return n*factorial(n-1);
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby LLVM での階乗計算の実装例 2323
m = LLVM::Module.new("Factorial")
m.functions.add("fac", [LLVM::Int], LLVM::Int) do |fac, n|
n.name = "n"
bb = fac.basic_blocks
entryBB = bb.append("entry")
recurBB = bb.append("recur")
resultBB = bb.append("result")
n_fac_n_1 = nil
entryBB.build do |builder|
test = builder.icmp(:eq, n, LLVM::Int(1), "test")
builder.cond(test, result, recur)
end
recurBB.build do |builder|
n_1 = builder.sub(n, LLVM::Int(1), "n-1")
fac_n_1 = builder.call(fac, n_1, "fac(n-1)")
n_fac_n_1 = builder.mul(n, fac_n_1, "n*fac(n-1)")
builder.br(result)
end
resultBB.build do |builder|
fac = builder.phi(LLVM::Int,
{entry => LLVM::Int(1),
recur => n_fac_n_1 },
"fac")
builder.ret(fac)
end
end
m = LLVM::Module.new("Factorial")
m.functions.add("fac", [LLVM::Int], LLVM::Int) do |fac, n|
n.name = "n"
bb = fac.basic_blocks
entryBB = bb.append("entry")
recurBB = bb.append("recur")
resultBB = bb.append("result")
n_fac_n_1 = nil
entryBB.build do |builder|
test = builder.icmp(:eq, n, LLVM::Int(1), "test")
builder.cond(test, result, recur)
end
recurBB.build do |builder|
n_1 = builder.sub(n, LLVM::Int(1), "n-1")
fac_n_1 = builder.call(fac, n_1, "fac(n-1)")
n_fac_n_1 = builder.mul(n, fac_n_1, "n*fac(n-1)")
builder.br(result)
end
resultBB.build do |builder|
fac = builder.phi(LLVM::Int,
{entry => LLVM::Int(1),
recur => n_fac_n_1 },
"fac")
builder.ret(fac)
end
end
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM での階乗計算 ポイント解説
LLVM::Builder#br( basic_block)
引数の basic_block に処理を移す。
LLVM::Builder#cond( test, result, recur)
test が true なら result, false なら recur に制御を移す
LLVM::Builder#phi(type, params, name)
複数の basic block から来たとき、どの値を採用するかを決める。
第2引数でどこから来れば、どんな値にするかハッシュで渡す。
2424
builder.cond(test, result, recur)builder.cond(test, result, recur)
builder.br(result)builder.br(result)
fac = builder.phi(LLVM::Int,
{entry => LLVM::Int(1),
recur => n_fac_n_1 },
"fac")
fac = builder.phi(LLVM::Int,
{entry => LLVM::Int(1),
recur => n_fac_n_1 },
"fac")
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
デモ
2525
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
まとめ (LLVM の概要と Ruby-LLVM)
LLVM はコンパイラを作成するための基盤
LLVM IR という中間表現は言語、アーキテクチャから独立
Module、Function、Basic Block の階層構造である
Module の中に Function がある。
Function の中に Basic Block がある。
Basic Block の中に個々の Instruction がある
Ruby-LLVM で LLVM で遊べる
LLVM IR を出力できる
JIT で LLVM を実行できる
Ruby-LLVM での条件分岐は LLVM IR と微妙に違う
LLVM IR は条件分岐も br。 Ruby-LLVM では cond メソッド
phi 関数の第2引数の順番が違う
2626
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Parslet と
Ruby-LLVM の
夢の競演
2727
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Parslet とは
Parslet
PEG (Parsing Expression Grammer) パーサ生成ライブラリ
Ruby の内部 DSL で PEG を表現する。
有力な対抗馬
TreeTop: Rubyっぽい独自言語を使う
citrus: Ruby っぽい独自言語を使う
rsec:Rubyの内部DSL だけど、なんかイマイチ。
強力な点
エラーレポート機能に注力
テストしやすいコード
parser と transformer の2層に明確に分離されている
抽象構文木が慣れ親しんだ Ruby のハッシュで表現されている
2828
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Parslet で電卓を実装してみる
仕様
計算できるのは、Int だけ
計算できる演算は +、-、*、/ の4つだけ
答えももちろん Int だけ
*、/ は +、- より優先順位が高い
出力、計算
Ruby-LLVM で LLVM のコードを出力させる
実際の計算はしないつもり
工夫している点
BasicBlock#build の引数のブロック内で、
LLVM の命令を追加していくことが必要。
この仕様は Transformer の rule と相性ワルし!
BasicBlock#build を別の Fiber で実行することで解決!
2929
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の Parser の実装 3030
class Parser < Parslet::Parser
rule(:space) { match('¥s').repeat(1) }
rule(:sp) { space.maybe }
rule(:integer) { match('[0-9]').repeat(1).as(:int) }
rule(:mul) {
integer.as(:left) >> sp >> match('[*/]').as(:op) >>
sp >> multiplication.as(:right)
}
rule(:add) {
multiplication.as(:left) >> sp >> match('[+-]').as(:op) >>
sp >> addition.as(:right)
}
rule(:multiplication) { mul | integer }
rule(:addition) { add | multiplication }
rule(:expression) { addition.as(:expr) }
root :expression
end
class Parser < Parslet::Parser
rule(:space) { match('¥s').repeat(1) }
rule(:sp) { space.maybe }
rule(:integer) { match('[0-9]').repeat(1).as(:int) }
rule(:mul) {
integer.as(:left) >> sp >> match('[*/]').as(:op) >>
sp >> multiplication.as(:right)
}
rule(:add) {
multiplication.as(:left) >> sp >> match('[+-]').as(:op) >>
sp >> addition.as(:right)
}
rule(:multiplication) { mul | integer }
rule(:addition) { add | multiplication }
rule(:expression) { addition.as(:expr) }
root :expression
end
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の Transformer の実装
LLVM 関係の処理は、LLVMBuilder クラスに外出し
rule を記述するだけで比較的簡単に実装可能
3131
class LLVMTransformer <Parslet::Transform
b = @@builder = LLVMBuilder.new
rule(:int => simple(:n)){
b.int n
}
rule(:left => simple(:l),
:right => simple(:r),
:op => simple(:op)){
b.calc op, l, r
}
rule(:expr => simple(x)){
b.ret x
}
def do(tree)
apply(tree)
@@builder.dump
end
end
class LLVMTransformer <Parslet::Transform
b = @@builder = LLVMBuilder.new
rule(:int => simple(:n)){
b.int n
}
rule(:left => simple(:l),
:right => simple(:r),
:op => simple(:op)){
b.calc op, l, r
}
rule(:expr => simple(x)){
b.ret x
}
def do(tree)
apply(tree)
@@builder.dump
end
end
パーサによって生成された
"int" 要素を処理。
LLVM の整数に変換。
パーサによって生成された
"int" 要素を処理。
LLVM の整数に変換。
パーサによって生成された
"left"、 "right"、 "op" の
要素を持つサブツリーに対して
処理を行う。
LLVM の命令に変換する。
パーサによって生成された
"left"、 "right"、 "op" の
要素を持つサブツリーに対して
処理を行う。
LLVM の命令に変換する。
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の LLVMBuilder の実装
LLVMBuilder では LLVM の命令を生成する。
building_loop で rule からの指示を待つ
3232
class LLVMBuilder
def initialize
@fiber = Fiber.new do
@module = LLVM::Module.new("calculator")
@module.functions.add("add", [], LLVM::Int) do |f,|
bb = f.basic_blocks.append("entry")
bb.build do |builder|
building_loop builder
end
end
end
@fiber.resume
end
...
end
class LLVMBuilder
def initialize
@fiber = Fiber.new do
@module = LLVM::Module.new("calculator")
@module.functions.add("add", [], LLVM::Int) do |f,|
bb = f.basic_blocks.append("entry")
bb.build do |builder|
building_loop builder
end
end
end
@fiber.resume
end
...
end
新しい Fiber を生成している。新しい Fiber を生成している。
1. Module の生成
2. Function の追加
3. Basic Block の追加
4. Builder を使い、LLVM 命令を追加
1. Module の生成
2. Function の追加
3. Basic Block の追加
4. Builder を使い、LLVM 命令を追加
building_loop まで Fiber を実行させておく。building_loop まで Fiber を実行させておく。
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の LLVMBuilder の実装 3333
def building_loop builder
llvm_ir = nil
loop do
op, l, r = Fiber.yield llvm_ir
case op
when "+"
llvm_ir = builder.add l, r
when "-"
llvm_ir = builder.sub l, r
when '*'
llvm_ir = builder.mul l, r
when "/"
llvm_ir = builder.sdiv l, r
when :exit
llvm_ir = builder.ret l
break
end
end
end
def building_loop builder
llvm_ir = nil
loop do
op, l, r = Fiber.yield llvm_ir
case op
when "+"
llvm_ir = builder.add l, r
when "-"
llvm_ir = builder.sub l, r
when '*'
llvm_ir = builder.mul l, r
when "/"
llvm_ir = builder.sdiv l, r
when :exit
llvm_ir = builder.ret l
break
end
end
end
Fiber から呼出し元への返り値みたいなもの。
Fiber なので、Fiber.yield の引数で処理結果を
渡して呼出し元に処理を戻す。
Fiber から呼出し元への返り値みたいなもの。
Fiber なので、Fiber.yield の引数で処理結果を
渡して呼出し元に処理を戻す。
呼出し元から来る引数みたいなもの。
Fiber なので、Fiber.yield の返り値で
呼出しから受け取る。
呼出し元から来る引数みたいなもの。
Fiber なので、Fiber.yield の返り値で
呼出しから受け取る。
四則演算の LLVM
の命令を生成
四則演算の LLVM
の命令を生成
計算結果を return する
LLVM の命令を生成
計算結果を return する
LLVM の命令を生成
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の LLVMBuilder の実装
Fiber の処理を calc 、ret というメソッドで隠ぺい
calc +、-、×、÷ の四則演算を LLVM の処理に変換する
ret 処理結果を LLVM の関数の返り値にする。
3434
def int n
LLVM.Int n.to_i
end
def calc op, l, r
@fiber.resume op, l, r
end
def ret x
@fiber.resume :exit, x
end
def dump
@module.dump
end
def int n
LLVM.Int n.to_i
end
def calc op, l, r
@fiber.resume op, l, r
end
def ret x
@fiber.resume :exit, x
end
def dump
@module.dump
end
Fiber の外から、演算を受け取って、
Fiber に渡す。
さらに Fiber での処理結果を返す。
Fiber の外から、演算を受け取って、
Fiber に渡す。
さらに Fiber での処理結果を返す。
Fiber がまだ動いていたら、
:exit を送って、終了させる。
BasicBlock#build のブロックの処理が
終わらないと、正常に動作しない。
Fiber がまだ動いていたら、
:exit を送って、終了させる。
BasicBlock#build のブロックの処理が
終わらないと、正常に動作しない。
Ruby の整数を LLVM の整数に
変換する。
Ruby の整数を LLVM の整数に
変換する。
処理を終了し、返り値として返す処理を終了し、返り値として返す
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
デモ
3535
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
まとめ ( Parslet と Ruby-LLVM の夢の競演)
Parslet とは
Ruby 用の PEG パーサ生成ライブラリ
他の同種のライブラリと比較して
内部DSL の書き方がイケてる
Ruby のふつうの Hash を抽象構文木に使うので扱いやすい
電卓の実装をしてみた。
作ってみたかんじ、案外カンタン
Ruby-LLVM の Basic Block への命令の追加が、
ブロック内で処理する仕様になっている
Parslet の DSL との相性がよくなかった
Fiber を使い、うまく解決
3636
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM の Pass について
3737
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
第6章 Pass について
Pass とは
LLVM IR に対して解析/最適化操作などを行うもの
Pass の種類
Immutable Pass
LLVM IR の変換を行わない Pass。最適化のための静的情報を提供
Module Pass
プログラム全体が対象の Pass。Function の追加/削除が可能。
Function Pass
プログラム中のすべての Function に対して実行される Pass。
Loop Pass
各Function のすべての Loop に対して実行される Pass。
Region Pass
各Function のすべての Region に対して実行される Pass。
BasicBlockPass
すべての BasicBlock(ジャンプのない一連の処理)に対して実行される Pass。
3838
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
easypass.hpp
FunctionPass の実装例として、EasyPass を紹介する
runOnFunction 仮想メンバ関数が Function ごとに
実行される
3939
#include<cstdio>
#include<llvm/Pass.h>
#include<llvm/Function.h>
using namespace llvm;
class EasyPass : public FunctionPass{
public:
static char ID;
EasyPass() : FunctionPass(ID){}
~EasyPass(){}
virtual bool runOnFunction(Function &F);
};
#include<cstdio>
#include<llvm/Pass.h>
#include<llvm/Function.h>
using namespace llvm;
class EasyPass : public FunctionPass{
public:
static char ID;
EasyPass() : FunctionPass(ID){}
~EasyPass(){}
virtual bool runOnFunction(Function &F);
};
FunctionPass や Pass の定義があるFunctionPass や Pass の定義がある
Function の定義があるFunction の定義がある
おまじないおまじない
Function ごとに実行
される仮想メンバ関数
Function ごとに実行
される仮想メンバ関数
EasyPass という
FunctionPass を新たに定義
EasyPass という
FunctionPass を新たに定義
llvm の名前空間を利用llvm の名前空間を利用
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
easypass.cpp
Function の名前を標準エラー出力に表示
テンプレートクラス RegisterPass のテンプレート引数
に EasyPass を渡してインスタンス化すると、
EasyPass が利用可能になる。
4040
#include "easypass.hpp"
bool EasyPass::runOnFunction(Function &F){
fprintf(stderr, "Function Name : %s¥n",
F.getName().str().c_str());
return false;
}
//
char EasyPass::ID=0;
static RegisterPass<EasyPass> X("easypass",
"easy pass:only print function name", false , false);
#include "easypass.hpp"
bool EasyPass::runOnFunction(Function &F){
fprintf(stderr, "Function Name : %s¥n",
F.getName().str().c_str());
return false;
}
//
char EasyPass::ID=0;
static RegisterPass<EasyPass> X("easypass",
"easy pass:only print function name", false , false);
前のスライドの easypass.hpp を利用前のスライドの easypass.hpp を利用
関数名を表示関数名を表示
RegisterPass テンプートクラスを
インスタンス化すると、EasyPass が
登録され、利用できるようになる。
第1引数: Pass の名前
第2引数: help への表示用文字列
第3引数: CFG を利用するが、
変更しない場合 true
第4引数: 解析用のパスなら true
RegisterPass テンプートクラスを
インスタンス化すると、EasyPass が
登録され、利用できるようになる。
第1引数: Pass の名前
第2引数: help への表示用文字列
第3引数: CFG を利用するが、
変更しない場合 true
第4引数: 解析用のパスなら true
クラス変数 ID を 初期化
※ 値はなんでもいいらしい
クラス変数 ID を 初期化
※ 値はなんでもいいらしい
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
easypass の実行 4141
$ LLVM_DIR=/usr/lib/llvm-3.1
$ CXXFLAGS=`${LLVM_DIR}/bin/llvm-config --cxxflags`
$ g++ -g ./src/easypass.cpp –I./inc ${CXXFLAGS} –c –o ./obj/easypass.o
$ g++ -g –shared –o ./bin/easypass.so ./obj/easypass.o
$ LLVM_DIR=/usr/lib/llvm-3.1
$ CXXFLAGS=`${LLVM_DIR}/bin/llvm-config --cxxflags`
$ g++ -g ./src/easypass.cpp –I./inc ${CXXFLAGS} –c –o ./obj/easypass.o
$ g++ -g –shared –o ./bin/easypass.so ./obj/easypass.o
$ ${LLVM_DIR}/bin/opt –load ./bin/easypass.so –easypass ¥
–debug-pass=Structure ./sample/test.ll –S > /dev/null
$ ${LLVM_DIR}/bin/opt –load ./bin/easypass.so –easypass ¥
–debug-pass=Structure ./sample/test.ll –S > /dev/null
Pass Arguments: -targetlibinfo -easypass -preverify -domtree -verify -print-module
Target Library Information
ModulePass Manager
FunctionPass Manager
easy pass:only print function name
Preliminary module verification
Dominator Tree Construction
Module Verifier
Print module to stderr
Function Name : test
Function Name : main
Pass Arguments: -targetlibinfo -easypass -preverify -domtree -verify -print-module
Target Library Information
ModulePass Manager
FunctionPass Manager
easy pass:only print function name
Preliminary module verification
Dominator Tree Construction
Module Verifier
Print module to stderr
Function Name : test
Function Name : main
-shared オプションで、共有オブジェクトにする。-shared オプションで、共有オブジェクトにする。
-load で so をロードすると、-easypassが使える。-load で so をロードすると、-easypassが使える。
-debug-pass=Structure の表示-debug-pass=Structure の表示
easypass による出力easypass による出力
Function Pass として easypass が実行されるFunction Pass として easypass が実行される
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
デモ
4242
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
ModulePass
ModulePass とは
プログラム全体を対象とする
Function 本体の参照、Function の追加/削除が可能
最適化処理、プログラムの解析などのために使われる
使い方
ModulePass を継承し、runOnModule をオーバーロード
最適化で、Module が修正された場合は true を返す。
ほかも同様に runOnXX というメソッドで目的の処理を行う。
4343
namespace llvm{
class ModulePass{
virtual bool runOnModule(Module &M) = 0;
}
}
namespace llvm{
class ModulePass{
virtual bool runOnModule(Module &M) = 0;
}
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
FunctionPass
FunctionPass とは
プログラムの中のすべての Function に対して実行される
一番よく使われる Pass
対象の Function 以外の Function の操作は不可
Module に Function の追加/削除不可
Module にグローバル変数の追加/削除不可
runOnFunction の実行にまたがった状態の維持
使い方
runOnFunction が Function ごとに呼び出される
Function が修正された場合は true を返す。
4444
namespace llvm{
class FunctionPass{
virtual bool doInitialization(Module &M);
virtual bool runOnFunction(Function &F) = 0;
virtual bool doFinalization(Module &M);
}
}
namespace llvm{
class FunctionPass{
virtual bool doInitialization(Module &M);
virtual bool runOnFunction(Function &F) = 0;
virtual bool doFinalization(Module &M);
}
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LoopPass
LoopPass とは
各Function のすべての Loop に対して実行される
多重ループの場合は内側から順に処理される
LPPassManager を使うといろいろできる
Loop のネスト構造の変更
Function や Module レベルの解析情報へのアクセス
使い方
runOnLoop が各 Function のすべての Loop で実行される
修正された場合は true を返す。
4545
namespace llvm{
class LoopPass{
virtual bool doInitialization(Loop *, LPPassManager &LPM);
virtual bool runOnLoop(Loop *, LPPassManager &LPM) = 0;
virtual bool doFinalization();
}
}
namespace llvm{
class LoopPass{
virtual bool doInitialization(Loop *, LPPassManager &LPM);
virtual bool runOnLoop(Loop *, LPPassManager &LPM) = 0;
virtual bool doFinalization();
}
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
RegionPass
RegionPass とは
LoopPass とよく似ている
出入り口を1つ持つ Region に対して実行される
ネストの場合は内側から順に処理される
RGPassManager を使うといろいろできる
RegionTree の変更
Function や Module レベルの解析情報へのアクセス
使い方
runOnRegion が各 Function のすべての Loop で実行される
修正された場合は true を返す。
4646
namespace llvm{
class RegionPass{
virtual bool doInitialization(Region *R, RGPassManager &RGM);
virtual bool runOnRegion(Region *R, RGPassManager &RGM) = 0;
virtual bool doFinalization();
}
}
namespace llvm{
class RegionPass{
virtual bool doInitialization(Region *R, RGPassManager &RGM);
virtual bool runOnRegion(Region *R, RGPassManager &RGM) = 0;
virtual bool doFinalization();
}
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
BasicBlockPass
BasicBlockPass とは
FunctionPass とよく似ている
単一の BasicBlock を参照や変更の対象とする。
※ 出入り口が1つでラベルがあり、ジャンプがない一連の処理
他の BasicBlock の参照や変更は禁止
使い方
runOnBasicBlock がすべての BasicBlock で実行される
修正された場合は true を返す。
4747
namespace llvm{
class BasicBlockPass{
virtual bool doInitialization(Function &F);
virtual bool runOnBasicBlock(BasicBlock &BB) = 0;
virtual bool doFinalization(Function &F);
}
}
namespace llvm{
class BasicBlockPass{
virtual bool doInitialization(Function &F);
virtual bool runOnBasicBlock(BasicBlock &BB) = 0;
virtual bool doFinalization(Function &F);
}
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Pass の実装に関してその他
Pass::getAnalysisUsage
他の Pass の実行結果を利用することができる
他の Pass の解析結果に影響がないことを伝達できる。
デフォルトでは、他の実行結果を利用しないし、
他の Pass の解析結果は無効化される
AnalysisUsage::addRequired<>
実装する Pass の事前に実行したい Pass を登録できる
AnalysisUsage::addRequiredTransive<>
Pass 内で別の Pass に実行処理を遷移させることができる
AnalysisUsage::addPreserved<>
影響のない Pass を指定し、必要のない再計算を回避できる。
Pass::getAnalysis<>
事前に addRequired で指定した Pass の実行結果を取得できる
RegisterPass<>
Pass を登録するときに使う。
4848
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Pass の例
下記のコマンドで opt で使われている Pass を確認可能
4949
$ llvm-as < /dev/null | ¥
opt -std-compile-opts -disable-output -debug-pass=Arguments
Pass Arguments: -targetlibinfo -no-aa -tbaa -basicaa -preverify -
domtree -verify -globalopt -ipsccp -deadargelim -instcombine -
simplifycfg -basiccg -prune-eh -inline -functionattrs -argpromotion
-scalarrepl-ssa -domtree -early-cse -simplify-libcalls -lazy-value-
info -jump-threading -correlated-propagation -simplifycfg -
instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops
-loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -
instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop-
idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt -
sccp -instcombine -lazy-value-info -jump-threading -correlated-
propagation -domtree -memdep -dse -adce -simplifycfg -instcombine -
strip-dead-prototypes -globaldce -constmerge -preverify -domtree -
verify
$ llvm-as < /dev/null | ¥
opt -std-compile-opts -disable-output -debug-pass=Arguments
Pass Arguments: -targetlibinfo -no-aa -tbaa -basicaa -preverify -
domtree -verify -globalopt -ipsccp -deadargelim -instcombine -
simplifycfg -basiccg -prune-eh -inline -functionattrs -argpromotion
-scalarrepl-ssa -domtree -early-cse -simplify-libcalls -lazy-value-
info -jump-threading -correlated-propagation -simplifycfg -
instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops
-loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch -
instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop-
idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt -
sccp -instcombine -lazy-value-info -jump-threading -correlated-
propagation -domtree -memdep -dse -adce -simplifycfg -instcombine -
strip-dead-prototypes -globaldce -constmerge -preverify -domtree -
verify
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
主要な最適化 Pass の概要
概要 処理内容の例
-instcombine 複数の命令を統合 2回の +1 を +2 に変更
-simplifycfg Call Flow Graph を単純化 不要な Basic Block の削除。
前後の Basic Block をマージ。
-inline 関数のインライン化 caller に関数の内容をマージ
-jump-threading 特定の分岐に必ず遷移すると
きに、ジャンプの数を減らす
if(true){ X=4; } if( X < 3 ){ … }
で、ジャンプの数を減らす。
-indvars ループ変数を正規化 for(i=7; i*I < 1000; ++i) を
for(i=0; i < 25; ++i) に変換
-licm ループ内で不変の計算をルー
プ外に移動
for( … ){ x = y + z; … } を
x = y + z; for( … ){ … } に変換
-loop-unswitch ループ内の条件分岐を条件分
岐内のループに変換
for( … ){ A; if(test) B; C} を
if(test){for(…) {A; B; C;}}
else{for(…) {A; C}} に変換
-loop-unroll ループの終了判定の負担を減
らすため、ループ展開を行う。
http://ja.wikipedia.org/wiki/
ループ展開
5050
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
instcombine の例
1 + 1 の計算がなくなり、直接 2 を返すようになっている
5151
int two(void){
int one = 1;
return one + 1;
}
int two(void){
int one = 1;
return one + 1;
}
; (snip)
define i32 @two() nounwind {
%one = alloca i32, align 4
store i32 1, i32* %one, align 4
%1 = load i32* %one, align 4
%2 = add nsw i32 %1, 1
ret i32 %2
}
; (snip)
define i32 @two() nounwind {
%one = alloca i32, align 4
store i32 1, i32* %one, align 4
%1 = load i32* %one, align 4
%2 = add nsw i32 %1, 1
ret i32 %2
}; (snip)
define i32 @two()
nounwind {
ret i32 2
}
; (snip)
define i32 @two()
nounwind {
ret i32 2
}
clang –cc1 instcombine.c –emit-llvmclang –cc1 instcombine.c –emit-llvm
opt –instcombine instcombine.ll | llvm-disopt –instcombine instcombine.ll | llvm-dis
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
inline の例
最適化により、関数 one() の呼出しがなくなり、さらに
その定義もなくなっている。
5252
static int one(void){
return 1;
}
int two(void){
return one() + 1;
}
static int one(void){
return 1;
}
int two(void){
return one() + 1;
}
ModuleID = 'inline.c'
target datalayout = "(snip)"
target triple = "x86_64-pc-linux-gnu"
define i32 @two() nounwind {
%1 = call i32 @one()
%2 = add nsw i32 %1, 1
ret i32 %2
}
define internal i32 @one() nounwind {
ret i32 1
}
ModuleID = 'inline.c'
target datalayout = "(snip)"
target triple = "x86_64-pc-linux-gnu"
define i32 @two() nounwind {
%1 = call i32 @one()
%2 = add nsw i32 %1, 1
ret i32 %2
}
define internal i32 @one() nounwind {
ret i32 1
}
; (snip)
define i32 @two()
nounwind {
%1 = add nsw i32 1, 1
ret i32 %1
}
; (snip)
define i32 @two()
nounwind {
%1 = add nsw i32 1, 1
ret i32 %1
}
opt –inline inline.ll | llvm-disopt –inline inline.ll | llvm-dis
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
simplifycfg の例
return 2 の処理が共通なので、Basic Block がマージ
されている。
5353
int two(void){
int one = 1;
if(one)
return 2;
else
return 2;
}
int two(void){
int one = 1;
if(one)
return 2;
else
return 2;
}
define i32 @two() nounwind {
%1 = alloca i32, align 4
%one = alloca i32, align 4
store i32 1, i32* %one, align 4
%2 = load i32* %one, align 4
%3 = icmp ne i32 %2, 0
br i1 %3, label %4, label %5
;
<label>:4
; preds = %0
store i32 2, i32* %1
br label %6
;
<label>:5
; preds = %0
store i32 2, i32* %1
br label %6
;
<label>:6
; preds = %5, %4
%7 = load i32* %1
ret i32 %7
}
define i32 @two() nounwind {
%1 = alloca i32, align 4
%one = alloca i32, align 4
store i32 1, i32* %one, align 4
%2 = load i32* %one, align 4
%3 = icmp ne i32 %2, 0
br i1 %3, label %4, label %5
;
<label>:4
; preds = %0
store i32 2, i32* %1
br label %6
;
<label>:5
; preds = %0
store i32 2, i32* %1
br label %6
;
<label>:6
; preds = %5, %4
%7 = load i32* %1
ret i32 %7
}
define i32 @two() nounwind {
%1 = alloca i32, align 4
%one = alloca i32, align 4
store i32 1, i32* %one, align 4
store i32 2, i32* %1
%2 = load i32* %1
ret i32 %2
}
define i32 @two() nounwind {
%1 = alloca i32, align 4
%one = alloca i32, align 4
store i32 1, i32* %one, align 4
store i32 2, i32* %1
%2 = load i32* %1
ret i32 %2
}
2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
まとめ(LLVM の Pass について)
LLVM の Pass で最適化、コードの解析が可能
言語、アーキテクチャから独立な最適化を実施
独自の Pass の実装も簡単
今回は紹介しなかったが、下記の機能もある
Call Flow Graph を dot でグラフ化することや
命令の種類ごとに、数をカウント
さらに LLVM の主要な最適化 Pass を紹介
instcombine : 複数の命令を1つにマージ
inline : みんな大好き関数のインライン化
simplifycfg : 複数の Basic Block のマージとか
あと、ループの展開、正規化、ループ内で不変の計算の外出し、
条件分岐とのネスト順の変更とかもある
5454
5555
ご清聴ありがとう
ございました

Weitere ähnliche Inhalte

Was ist angesagt?

ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門Fixstars Corporation
 
条件分岐とcmovとmaxps
条件分岐とcmovとmaxps条件分岐とcmovとmaxps
条件分岐とcmovとmaxpsMITSUNARI Shigeo
 
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールC/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールMITSUNARI Shigeo
 
【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Models【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Modelscvpaper. challenge
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Masahito Zembutsu
 
【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models
【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models
【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat ModelsDeep Learning JP
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門Norishige Fukushima
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpsonickun
 
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Etsuji Nakai
 
数学カフェ 確率・統計・機械学習回 「速習 確率・統計」
数学カフェ 確率・統計・機械学習回 「速習 確率・統計」数学カフェ 確率・統計・機械学習回 「速習 確率・統計」
数学カフェ 確率・統計・機械学習回 「速習 確率・統計」Ken'ichi Matsui
 
自動定理証明の紹介
自動定理証明の紹介自動定理証明の紹介
自動定理証明の紹介Masahiro Sakai
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015CODE BLUE
 
Rustに触れて私のPythonはどう変わったか
Rustに触れて私のPythonはどう変わったかRustに触れて私のPythonはどう変わったか
Rustに触れて私のPythonはどう変わったかShunsukeNakamura17
 
僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達Yusuke Wada
 
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)Shota Imai
 
AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解MITSUNARI Shigeo
 

Was ist angesagt? (20)

LLVM最適化のこつ
LLVM最適化のこつLLVM最適化のこつ
LLVM最適化のこつ
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門ARM CPUにおけるSIMDを用いた高速計算入門
ARM CPUにおけるSIMDを用いた高速計算入門
 
条件分岐とcmovとmaxps
条件分岐とcmovとmaxps条件分岐とcmovとmaxps
条件分岐とcmovとmaxps
 
C/C++プログラマのための開発ツール
C/C++プログラマのための開発ツールC/C++プログラマのための開発ツール
C/C++プログラマのための開発ツール
 
【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Models【メタサーベイ】基盤モデル / Foundation Models
【メタサーベイ】基盤モデル / Foundation Models
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
 
【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models
【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models
【DL輪読会】Llama 2: Open Foundation and Fine-Tuned Chat Models
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
 
RSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjpRSA暗号運用でやってはいけない n のこと #ssmjp
RSA暗号運用でやってはいけない n のこと #ssmjp
 
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門
 
数学カフェ 確率・統計・機械学習回 「速習 確率・統計」
数学カフェ 確率・統計・機械学習回 「速習 確率・統計」数学カフェ 確率・統計・機械学習回 「速習 確率・統計」
数学カフェ 確率・統計・機械学習回 「速習 確率・統計」
 
自動定理証明の紹介
自動定理証明の紹介自動定理証明の紹介
自動定理証明の紹介
 
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
Master Canary Forging: 新しいスタックカナリア回避手法の提案 by 小池 悠生 - CODE BLUE 2015
 
Rustに触れて私のPythonはどう変わったか
Rustに触れて私のPythonはどう変わったかRustに触れて私のPythonはどう変わったか
Rustに触れて私のPythonはどう変わったか
 
僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達
 
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
 
レシピの作り方入門
レシピの作り方入門レシピの作り方入門
レシピの作り方入門
 
AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解AVX-512(フォーマット)詳解
AVX-512(フォーマット)詳解
 
MLOps入門
MLOps入門MLOps入門
MLOps入門
 

Ähnlich wie きつねさんでもわかるLlvm読書会 第2回

Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7Takaaki Suzuki
 
Groovyで楽にSQLを実行してみよう
Groovyで楽にSQLを実行してみようGroovyで楽にSQLを実行してみよう
Groovyで楽にSQLを実行してみようAkira Shimosako
 
怠惰なRubyistへの道 fukuoka rubykaigi01
怠惰なRubyistへの道 fukuoka rubykaigi01怠惰なRubyistへの道 fukuoka rubykaigi01
怠惰なRubyistへの道 fukuoka rubykaigi01nagachika t
 
フィボナッチ数列の作り方
フィボナッチ数列の作り方フィボナッチ数列の作り方
フィボナッチ数列の作り方Tomoya Kawanishi
 
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptxTypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptxssuser8b389c
 
ジャパネットQB GPars
ジャパネットQB GParsジャパネットQB GPars
ジャパネットQB GParsTakahiro Sugiura
 
Easy caching and logging package using annotation in Python
Easy caching and logging package using annotation in PythonEasy caching and logging package using annotation in Python
Easy caching and logging package using annotation in PythonYasunori Horikoshi
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門伸男 伊藤
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Hideki Saito
 
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会Koichi Sakata
 
自作saoriの紹介
自作saoriの紹介自作saoriの紹介
自作saoriの紹介wizstargaer
 
My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1Makoto Haruyama
 
Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版Daiyu Hatakeyama
 
復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0Yuta Matsumura
 

Ähnlich wie きつねさんでもわかるLlvm読書会 第2回 (20)

Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7Live Coding で学ぶ C# 7
Live Coding で学ぶ C# 7
 
VerilatorとSystemC
VerilatorとSystemCVerilatorとSystemC
VerilatorとSystemC
 
Groovyで楽にSQLを実行してみよう
Groovyで楽にSQLを実行してみようGroovyで楽にSQLを実行してみよう
Groovyで楽にSQLを実行してみよう
 
怠惰なRubyistへの道 fukuoka rubykaigi01
怠惰なRubyistへの道 fukuoka rubykaigi01怠惰なRubyistへの道 fukuoka rubykaigi01
怠惰なRubyistへの道 fukuoka rubykaigi01
 
フィボナッチ数列の作り方
フィボナッチ数列の作り方フィボナッチ数列の作り方
フィボナッチ数列の作り方
 
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptxTypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
TypeScriptで書くLambdaをCDKでいい感じに管理する.pptx
 
ジャパネットQB GPars
ジャパネットQB GParsジャパネットQB GPars
ジャパネットQB GPars
 
Easy caching and logging package using annotation in Python
Easy caching and logging package using annotation in PythonEasy caching and logging package using annotation in Python
Easy caching and logging package using annotation in Python
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021
 
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会
 
自作saoriの紹介
自作saoriの紹介自作saoriの紹介
自作saoriの紹介
 
Puppet on AWS
Puppet on AWSPuppet on AWS
Puppet on AWS
 
Objc lambda
Objc lambdaObjc lambda
Objc lambda
 
qmake入門
qmake入門qmake入門
qmake入門
 
Embulk 20150411
Embulk 20150411Embulk 20150411
Embulk 20150411
 
emc++ chapter32
emc++ chapter32emc++ chapter32
emc++ chapter32
 
My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1My sql casual_in_fukuoka_vol1
My sql casual_in_fukuoka_vol1
 
Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版Azure Machine Learning Services 概要 - 2019年2月版
Azure Machine Learning Services 概要 - 2019年2月版
 
復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0復習も兼ねて!C#6.0-7.0
復習も兼ねて!C#6.0-7.0
 

Mehr von Tomoya Kawanishi

ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例Tomoya Kawanishi
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウTomoya Kawanishi
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列についてTomoya Kawanishi
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてTomoya Kawanishi
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されることTomoya Kawanishi
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler についてTomoya Kawanishi
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現についてTomoya Kawanishi
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行についてTomoya Kawanishi
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについてTomoya Kawanishi
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことTomoya Kawanishi
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選Tomoya Kawanishi
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと WebクローリングについてTomoya Kawanishi
 
Active record query interface
Active record query interfaceActive record query interface
Active record query interfaceTomoya Kawanishi
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能についてTomoya Kawanishi
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーTomoya Kawanishi
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO についてTomoya Kawanishi
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介Tomoya Kawanishi
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてTomoya Kawanishi
 

Mehr von Tomoya Kawanishi (20)

英単語の覚え方
英単語の覚え方英単語の覚え方
英単語の覚え方
 
ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウ
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列について
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構について
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されること
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler について
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現について
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行について
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについて
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったこと
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと Webクローリングについて
 
Rake
RakeRake
Rake
 
Active record query interface
Active record query interfaceActive record query interface
Active record query interface
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能について
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO について
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについて
 

Kürzlich hochgeladen

CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案sugiuralab
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)Hiroshi Tomioka
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 

Kürzlich hochgeladen (8)

CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 

きつねさんでもわかるLlvm読書会 第2回

  • 2. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 自己紹介 最近のできごと Ruby勉強会で TRICK に関する発表をしました。 TRICK: 超絶技巧Ruby意味不明コンテスト IOCCC の Ruby版 やっつけで資料を作ったわりにはみなさんから 好評だったようでよかったです いろんな活動を始めました 山登り クーリエジャポン朝食会 次回の勉強会 次回の amagasakirb は 8月10日(土)で考えています。 Land of Lisp の読書会の予定です。 11
  • 3. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 今日の構成 3本立てです。 LLVM 概要と Ruby-LLVM LLVM がどんなものかについて簡単に紹介 LLVM のコードを生成する Ruby-LLVM ライブラリを紹介 Hello, World 階乗を計算 Parslet と Ruby-LLVM の夢の競演 PEG パーサ生成器 Parslet を紹介 Parslet で電卓プログラムを作成 四則演算を正しく処理する LLVM の Pass について Pass の作り方 各種 Pass の概要を紹介 主要な最適化 Pass について紹介 22
  • 4. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM概要と Ruby-LLVM 33
  • 5. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM とは LLVM とは コンパイラを作成するための共通基盤 BSDライセンス で C++ で開発 多くのサブプロジェクトから構成される LLVM Core、Clang、 etc LLVM Core とは 言語やアーキテクチャから独立した中間表現 LLVM IR を規定 新言語の開発で字句解析や構文解析だけを作成し、他は流用できる 最適化や新たなアーキテクチャ対応の試作なども可能 さまざまな最適化、多くのアーキテクチャへのコード生成に対応 LLVM IR のレベルで最適化するため、言語やアーキテクチャから独立 C++ 等の言語へのフロントエンド実装 Clang が有名 LLVM をコンパイラ基盤ではなくコンパイラとして利用可能 44
  • 6. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 環境構築 Debian 7.0 (Wheezy) で環境を構築 aptitude で簡単にインストール 単に、 aptitude install llvm とすると 3.0 になった。 llvm-3.1 では、 -3.1 という suffix に要注意 例: llvm-config ではなく llvm-config-3.1 PATH を通すか alias が必要 55 sudo aptitude install llvm-3.1 llvm-3.1-dev llvm-3.1-doc llvm-3.1-examples llvm-3.1-runtime llvm-3.1-source $ export PATH=/usr/lib/llvm-3.1/bin:$PATH ### LLVM の最新版を使いたいなら $ sudo cat >> /etc/apt/sources.list deb http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main deb-src http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main ^D $ sudo aptitude install llvm-3.3 … sudo aptitude install llvm-3.1 llvm-3.1-dev llvm-3.1-doc llvm-3.1-examples llvm-3.1-runtime llvm-3.1-source $ export PATH=/usr/lib/llvm-3.1/bin:$PATH ### LLVM の最新版を使いたいなら $ sudo cat >> /etc/apt/sources.list deb http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main deb-src http://llvm.org/apt/wheezy/ llvm-toolchain-wheezy main ^D $ sudo aptitude install llvm-3.3 …
  • 7. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM Ruby で LLVM の実行コードの生成を行うライブラリ。 できること LLVM IR のコードの出力 LLVM JIT Compiler を使えば、その場で実行もできる オレオレ言語の開発がカンタン パーサ: treetop、citrus、rsec、parslet コードジェネレータ: Ruby-LLVM インストール方法 66 ### Ruby-LLVM のインストール $ export PATH=/usr/lib/llvm-3.1/bin:$PATH $ gem install ruby-llvm ### Ruby-LLVM のインストール $ export PATH=/usr/lib/llvm-3.1/bin:$PATH $ gem install ruby-llvm
  • 8. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 (復習) 簡単な LLVM IR の例 http://www.aosabook.org/en/llvm.html 77 define i32 @add1(i32 %a, i32 %b) { entry: %tmp1 = add i32 %a, %b ret i32 %tmp1 } define i32 @add2(i32 %a, i32 %b) { entry: %tmp1 = icmp eq i32 %a, 0 br i1 %tmp1, label %done, label %recurse recurse: %tmp2 = sub i32 %a, 1 %tmp3 = add i32 %b, 1 %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3) ret i32 %tmp4 done: ret i32 %b } define i32 @add1(i32 %a, i32 %b) { entry: %tmp1 = add i32 %a, %b ret i32 %tmp1 } define i32 @add2(i32 %a, i32 %b) { entry: %tmp1 = icmp eq i32 %a, 0 br i1 %tmp1, label %done, label %recurse recurse: %tmp2 = sub i32 %a, 1 %tmp3 = add i32 %b, 1 %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3) ret i32 %tmp4 done: ret i32 %b } unsigned add1(unsigned a, unsigned b) { return a+b; } // not efficient way to add two numbers. unsigned add2(unsigned a, unsigned b) { if (a == 0) return b; return add2(a-1, b+1); } unsigned add1(unsigned a, unsigned b) { return a+b; } // not efficient way to add two numbers. unsigned add2(unsigned a, unsigned b) { if (a == 0) return b; return add2(a-1, b+1); }
  • 9. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 (復習) Module, Function, Basic Block, Instruction 88 define i32 @add1(i32 %a, i32 %b) { entry: %tmp1 = add i32 %a, %b ret i32 %tmp1 } define i32 @add2(i32 %a, i32 %b) { entry: %tmp1 = icmp eq i32 %a, 0 br i1 %tmp1, label %done, label %recurse recurse: %tmp2 = sub i32 %a, 1 %tmp3 = add i32 %b, 1 %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3) ret i32 %tmp4 done: ret i32 %b } define i32 @add1(i32 %a, i32 %b) { entry: %tmp1 = add i32 %a, %b ret i32 %tmp1 } define i32 @add2(i32 %a, i32 %b) { entry: %tmp1 = icmp eq i32 %a, 0 br i1 %tmp1, label %done, label %recurse recurse: %tmp2 = sub i32 %a, 1 %tmp3 = add i32 %b, 1 %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3) ret i32 %tmp4 done: ret i32 %b } ModuleFunction Basic Block Instruction
  • 10. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 (復習) Module, Function, Basic Block, Instruction 1つのソースコードは LLVM IR では Module に対応する Module は Function や GlobalVariable などで構成される Function いわゆる関数 or メソッド 引数があって、一連の処理を行って、返り値を返す 複数の Basic Block で構成される Basic Block 1つの入り口と、1つの出口がある 終端命令の例: ret、br、switch Basic Block の最後には 1つの終端命令がある Basic Block を表す名前(ラベル)が付いている その名前で、別の Basic Block にジャンプできる 複数の Instruction で構成される Instruction 四則演算、ビット演算、型変換、条件分岐、メモリアクセスなど 処理の最小単位 99
  • 11. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での LLVM IR の生成 LLVM::Module を作る Module に グローバル変数、外部関数の宣言などを追加する Module に Function を追加する Module#functions.add メソッドを使う 追加する Function を作る プロトタイプを書く Function に BasicBlock を追加する Function#basic_blocks.append メソッドを使う 追加する BasicBlock を build する Builder を使って、各種の Instruction を追加する Instruction は四則演算とか、関数呼び出しとか return とか 環境を指定して、JIT で実行することもできる LLVM.init_x86 ; おまじない JITCompiler.new(m).run_function(func, args) 引数 args で モジュール m の関数 func を実行する。 1010
  • 12. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM で Hello, World Hello, World を出力する例を Ruby-LLVM で再実装する。 http://llvm.org/docs/LangRef.html#module-structure 1111 ; Declare the string constant as a global constant. @.str = private unnamed_addr constant [13 x i8] c"hello world¥0A¥00" ; External declaration of the puts function declare i32 @puts(i8* nocapture) nounwind ; Definition of main function define i32 @main() { ; i32()* ; Convert [13 x i8]* to i8 *... %cast210 = getelementptr [13 x i8]* @.str, i64 0, i64 0 ; Call puts function to write out the string to stdout. call i32 @puts(i8* %cast210) ret i32 0 } ; Named metadata !1 = metadata !{i32 42} !foo = !{!1, null} ; Declare the string constant as a global constant. @.str = private unnamed_addr constant [13 x i8] c"hello world¥0A¥00" ; External declaration of the puts function declare i32 @puts(i8* nocapture) nounwind ; Definition of main function define i32 @main() { ; i32()* ; Convert [13 x i8]* to i8 *... %cast210 = getelementptr [13 x i8]* @.str, i64 0, i64 0 ; Call puts function to write out the string to stdout. call i32 @puts(i8* %cast210) ret i32 0 } ; Named metadata !1 = metadata !{i32 42} !foo = !{!1, null}
  • 13. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World 実装例 1212 gem 'ruby-llvm' require 'llvm/core' require 'llvm/execution_engine' m = LLVM::Module.new('hello') str = "Hello, World!" llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1) llvm_g_str = m.globals.add(llvm_str_type, ".str") llvm_g_str.initializer = LLVM::ConstantArray.string(str) arg_types = [LLVM.Pointer(LLVM::Int8)] cputs = m.functions.add('puts', arg_types, LLVM::Int32) # Definition of main function main = m.functions.add('main', [], LLVM::Int32) do |function| entryBB = function.basic_blocks.append entryBB.build do |builder| zero = LLVM.Int(0) # GetElementPointer(gep) cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210' builder.call cputs, cast210 builder.ret zero end end m.verify m.dump puts "-----------------------------------------------------" LLVM.init_x86 LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[]) gem 'ruby-llvm' require 'llvm/core' require 'llvm/execution_engine' m = LLVM::Module.new('hello') str = "Hello, World!" llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1) llvm_g_str = m.globals.add(llvm_str_type, ".str") llvm_g_str.initializer = LLVM::ConstantArray.string(str) arg_types = [LLVM.Pointer(LLVM::Int8)] cputs = m.functions.add('puts', arg_types, LLVM::Int32) # Definition of main function main = m.functions.add('main', [], LLVM::Int32) do |function| entryBB = function.basic_blocks.append entryBB.build do |builder| zero = LLVM.Int(0) # GetElementPointer(gep) cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210' builder.call cputs, cast210 builder.ret zero end end m.verify m.dump puts "-----------------------------------------------------" LLVM.init_x86 LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
  • 14. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 1313
  • 15. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (1) おまじない 変数 m が新しく生成する LLVM の Module 1414 gem 'ruby-llvm' require 'llvm/core' require 'llvm/execution_engine' m = LLVM::Module.new('hello') gem 'ruby-llvm' require 'llvm/core' require 'llvm/execution_engine' m = LLVM::Module.new('hello')
  • 16. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (2) グローバル変数を hello Module に追加 LLVM で文字列リテラルは、 LLVM.Array(LLVM::Int8, str.size + 1) の型になる。 m.globals.add でグローバル変数を追加できる。 グローバル変数の名前は ".str" さらに ".str" を "Hello, World!" で初期化する。 1515 # generate global string "Hello, World!" str = "Hello, World!" llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1) llvm_g_str = m.globals.add(llvm_str_type, ".str") llvm_g_str.initializer = LLVM::ConstantArray.String(str) # generate global string "Hello, World!" str = "Hello, World!" llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1) llvm_g_str = m.globals.add(llvm_str_type, ".str") llvm_g_str.initializer = LLVM::ConstantArray.String(str)
  • 17. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (3) 外部で定義された関数 puts を宣言する。 m.functions LLVM::Module::FunctionCollection オブジェクト FunctionCollection#add メソッド 第1引数: 外部関数の名前 第2引数: LLVM 上の引数の型を表す配列 第3引数: LLVM 上の返り値の型 返り値: LLVM::Function オブジェクト 1616 # External Declaration of the `puts` function arg_types = [LLVM.Pointer(LLVM::Int8)] cputs = m.functions.add('puts', arg_types, LLVM::Int32) # External Declaration of the `puts` function arg_types = [LLVM.Pointer(LLVM::Int8)] cputs = m.functions.add('puts', arg_types, LLVM::Int32) extern int puts(const char *s);extern int puts(const char *s);
  • 18. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (4) メイン関数の定義 function.basic_blocks : LLVM::Function::BasicBlockCollection オブジェクト BasicBlock#build : LLVM::Builder オブジェクトを生成してブロックに渡す builder.gep: 複雑なデータ構造での格納場所(アドレス)を取得する。 1717 # Definition of main function main = m.functions.add('main', [], LLVM::Int32) do |function| entryBB = function.basic_blocks.append entryBB.build do |builder| zero = LLVM.Int(0) # GetElementPointer(gep) cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210' builder.call cputs, cast210 builder.ret zero end end # Definition of main function main = m.functions.add('main', [], LLVM::Int32) do |function| entryBB = function.basic_blocks.append entryBB.build do |builder| zero = LLVM.Int(0) # GetElementPointer(gep) cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210' builder.call cputs, cast210 builder.ret zero end end
  • 19. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (5) LLVM::Module#verify で Module が valid かを 検証できる。 LLVM::Module#dump で Module の IR を出力可能 標準エラー出力で出力される 1818 m.verify m.dump m.verify m.dump
  • 20. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (6) LLVM.init_x86 出力ターゲットのアーキテクチャを指定する。 JITCompiler.new(m).run_function(main, args) main 関数を実行する。 1919 LLVM.init_x86 LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[]) LLVM.init_x86 LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
  • 21. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World 実装例(再掲) 2020 gem 'ruby-llvm' require 'llvm/core' require 'llvm/execution_engine' m = LLVM::Module.new('hello') str = "Hello, World!" llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1) llvm_g_str = m.globals.add(llvm_str_type, ".str") llvm_g_str.initializer = LLVM::ConstantArray.string(str) arg_types = [LLVM.Pointer(LLVM::Int8)] cputs = m.functions.add('puts', arg_types, LLVM::Int32) # Definition of main function main = m.functions.add('main', [], LLVM::Int32) do |function| entryBB = function.basic_blocks.append entryBB.build do |builder| zero = LLVM.Int(0) # GetElementPointer(gep) cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210' builder.call cputs, cast210 builder.ret zero end end m.verify m.dump puts "-----------------------------------------------------" LLVM.init_x86 LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[]) gem 'ruby-llvm' require 'llvm/core' require 'llvm/execution_engine' m = LLVM::Module.new('hello') str = "Hello, World!" llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1) llvm_g_str = m.globals.add(llvm_str_type, ".str") llvm_g_str.initializer = LLVM::ConstantArray.string(str) arg_types = [LLVM.Pointer(LLVM::Int8)] cputs = m.functions.add('puts', arg_types, LLVM::Int32) # Definition of main function main = m.functions.add('main', [], LLVM::Int32) do |function| entryBB = function.basic_blocks.append entryBB.build do |builder| zero = LLVM.Int(0) # GetElementPointer(gep) cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210' builder.call cputs, cast210 builder.ret zero end end m.verify m.dump puts "-----------------------------------------------------" LLVM.init_x86 LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
  • 22. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World の出力 Ruby-LLVM で出力した LLVM IR だいたい再現できている。 2121 ; ModuleID = 'hello' @.str = global [14 x i8] c"Hello, World!¥00" declare i32 @puts(i8*) define i32 @main() { %1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ret i32 0 } ----------------------------------------------------- Hello, World! ; ModuleID = 'hello' @.str = global [14 x i8] c"Hello, World!¥00" declare i32 @puts(i8*) define i32 @main() { %1 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0)) ret i32 0 } ----------------------------------------------------- Hello, World!
  • 23. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM での階乗計算 LLVM IR での階乗計算を例に条件分岐を説明する br 命令で他の Basic Block に処理を遷移できる phi 命令でどの Basic Block 経由かで値を変えられる 2222 ; ModuleID = 'Factorial' define i32 @fac(i32 %n) { entry: %test = icmp eq i32 %n, 1 br i1 %test, label %result, label %recur recur: ; preds = %entry %n-1 = sub i32 %n, 1 %"fac(n-1)" = call i32 @fac(i32 %n-1) %"n*fac(n-1)" = mul i32 %n, %"fac(n-1)" br label %result result: ; preds = %recur, %entry %fac = phi i32 [ 1, %entry ], [ %"n*fac(n-1)", %recur ] ret i32 %fac } ; ModuleID = 'Factorial' define i32 @fac(i32 %n) { entry: %test = icmp eq i32 %n, 1 br i1 %test, label %result, label %recur recur: ; preds = %entry %n-1 = sub i32 %n, 1 %"fac(n-1)" = call i32 @fac(i32 %n-1) %"n*fac(n-1)" = mul i32 %n, %"fac(n-1)" br label %result result: ; preds = %recur, %entry %fac = phi i32 [ 1, %entry ], [ %"n*fac(n-1)", %recur ] ret i32 %fac } int factorial(int n) { if (n == 0) return 1; return n*factorial(n-1); } int factorial(int n) { if (n == 0) return 1; return n*factorial(n-1); }
  • 24. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby LLVM での階乗計算の実装例 2323 m = LLVM::Module.new("Factorial") m.functions.add("fac", [LLVM::Int], LLVM::Int) do |fac, n| n.name = "n" bb = fac.basic_blocks entryBB = bb.append("entry") recurBB = bb.append("recur") resultBB = bb.append("result") n_fac_n_1 = nil entryBB.build do |builder| test = builder.icmp(:eq, n, LLVM::Int(1), "test") builder.cond(test, result, recur) end recurBB.build do |builder| n_1 = builder.sub(n, LLVM::Int(1), "n-1") fac_n_1 = builder.call(fac, n_1, "fac(n-1)") n_fac_n_1 = builder.mul(n, fac_n_1, "n*fac(n-1)") builder.br(result) end resultBB.build do |builder| fac = builder.phi(LLVM::Int, {entry => LLVM::Int(1), recur => n_fac_n_1 }, "fac") builder.ret(fac) end end m = LLVM::Module.new("Factorial") m.functions.add("fac", [LLVM::Int], LLVM::Int) do |fac, n| n.name = "n" bb = fac.basic_blocks entryBB = bb.append("entry") recurBB = bb.append("recur") resultBB = bb.append("result") n_fac_n_1 = nil entryBB.build do |builder| test = builder.icmp(:eq, n, LLVM::Int(1), "test") builder.cond(test, result, recur) end recurBB.build do |builder| n_1 = builder.sub(n, LLVM::Int(1), "n-1") fac_n_1 = builder.call(fac, n_1, "fac(n-1)") n_fac_n_1 = builder.mul(n, fac_n_1, "n*fac(n-1)") builder.br(result) end resultBB.build do |builder| fac = builder.phi(LLVM::Int, {entry => LLVM::Int(1), recur => n_fac_n_1 }, "fac") builder.ret(fac) end end
  • 25. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM での階乗計算 ポイント解説 LLVM::Builder#br( basic_block) 引数の basic_block に処理を移す。 LLVM::Builder#cond( test, result, recur) test が true なら result, false なら recur に制御を移す LLVM::Builder#phi(type, params, name) 複数の basic block から来たとき、どの値を採用するかを決める。 第2引数でどこから来れば、どんな値にするかハッシュで渡す。 2424 builder.cond(test, result, recur)builder.cond(test, result, recur) builder.br(result)builder.br(result) fac = builder.phi(LLVM::Int, {entry => LLVM::Int(1), recur => n_fac_n_1 }, "fac") fac = builder.phi(LLVM::Int, {entry => LLVM::Int(1), recur => n_fac_n_1 }, "fac")
  • 26. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 2525
  • 27. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 まとめ (LLVM の概要と Ruby-LLVM) LLVM はコンパイラを作成するための基盤 LLVM IR という中間表現は言語、アーキテクチャから独立 Module、Function、Basic Block の階層構造である Module の中に Function がある。 Function の中に Basic Block がある。 Basic Block の中に個々の Instruction がある Ruby-LLVM で LLVM で遊べる LLVM IR を出力できる JIT で LLVM を実行できる Ruby-LLVM での条件分岐は LLVM IR と微妙に違う LLVM IR は条件分岐も br。 Ruby-LLVM では cond メソッド phi 関数の第2引数の順番が違う 2626
  • 28. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet と Ruby-LLVM の 夢の競演 2727
  • 29. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet とは Parslet PEG (Parsing Expression Grammer) パーサ生成ライブラリ Ruby の内部 DSL で PEG を表現する。 有力な対抗馬 TreeTop: Rubyっぽい独自言語を使う citrus: Ruby っぽい独自言語を使う rsec:Rubyの内部DSL だけど、なんかイマイチ。 強力な点 エラーレポート機能に注力 テストしやすいコード parser と transformer の2層に明確に分離されている 抽象構文木が慣れ親しんだ Ruby のハッシュで表現されている 2828
  • 30. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet で電卓を実装してみる 仕様 計算できるのは、Int だけ 計算できる演算は +、-、*、/ の4つだけ 答えももちろん Int だけ *、/ は +、- より優先順位が高い 出力、計算 Ruby-LLVM で LLVM のコードを出力させる 実際の計算はしないつもり 工夫している点 BasicBlock#build の引数のブロック内で、 LLVM の命令を追加していくことが必要。 この仕様は Transformer の rule と相性ワルし! BasicBlock#build を別の Fiber で実行することで解決! 2929
  • 31. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の Parser の実装 3030 class Parser < Parslet::Parser rule(:space) { match('¥s').repeat(1) } rule(:sp) { space.maybe } rule(:integer) { match('[0-9]').repeat(1).as(:int) } rule(:mul) { integer.as(:left) >> sp >> match('[*/]').as(:op) >> sp >> multiplication.as(:right) } rule(:add) { multiplication.as(:left) >> sp >> match('[+-]').as(:op) >> sp >> addition.as(:right) } rule(:multiplication) { mul | integer } rule(:addition) { add | multiplication } rule(:expression) { addition.as(:expr) } root :expression end class Parser < Parslet::Parser rule(:space) { match('¥s').repeat(1) } rule(:sp) { space.maybe } rule(:integer) { match('[0-9]').repeat(1).as(:int) } rule(:mul) { integer.as(:left) >> sp >> match('[*/]').as(:op) >> sp >> multiplication.as(:right) } rule(:add) { multiplication.as(:left) >> sp >> match('[+-]').as(:op) >> sp >> addition.as(:right) } rule(:multiplication) { mul | integer } rule(:addition) { add | multiplication } rule(:expression) { addition.as(:expr) } root :expression end
  • 32. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の Transformer の実装 LLVM 関係の処理は、LLVMBuilder クラスに外出し rule を記述するだけで比較的簡単に実装可能 3131 class LLVMTransformer <Parslet::Transform b = @@builder = LLVMBuilder.new rule(:int => simple(:n)){ b.int n } rule(:left => simple(:l), :right => simple(:r), :op => simple(:op)){ b.calc op, l, r } rule(:expr => simple(x)){ b.ret x } def do(tree) apply(tree) @@builder.dump end end class LLVMTransformer <Parslet::Transform b = @@builder = LLVMBuilder.new rule(:int => simple(:n)){ b.int n } rule(:left => simple(:l), :right => simple(:r), :op => simple(:op)){ b.calc op, l, r } rule(:expr => simple(x)){ b.ret x } def do(tree) apply(tree) @@builder.dump end end パーサによって生成された "int" 要素を処理。 LLVM の整数に変換。 パーサによって生成された "int" 要素を処理。 LLVM の整数に変換。 パーサによって生成された "left"、 "right"、 "op" の 要素を持つサブツリーに対して 処理を行う。 LLVM の命令に変換する。 パーサによって生成された "left"、 "right"、 "op" の 要素を持つサブツリーに対して 処理を行う。 LLVM の命令に変換する。
  • 33. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の LLVMBuilder の実装 LLVMBuilder では LLVM の命令を生成する。 building_loop で rule からの指示を待つ 3232 class LLVMBuilder def initialize @fiber = Fiber.new do @module = LLVM::Module.new("calculator") @module.functions.add("add", [], LLVM::Int) do |f,| bb = f.basic_blocks.append("entry") bb.build do |builder| building_loop builder end end end @fiber.resume end ... end class LLVMBuilder def initialize @fiber = Fiber.new do @module = LLVM::Module.new("calculator") @module.functions.add("add", [], LLVM::Int) do |f,| bb = f.basic_blocks.append("entry") bb.build do |builder| building_loop builder end end end @fiber.resume end ... end 新しい Fiber を生成している。新しい Fiber を生成している。 1. Module の生成 2. Function の追加 3. Basic Block の追加 4. Builder を使い、LLVM 命令を追加 1. Module の生成 2. Function の追加 3. Basic Block の追加 4. Builder を使い、LLVM 命令を追加 building_loop まで Fiber を実行させておく。building_loop まで Fiber を実行させておく。
  • 34. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の LLVMBuilder の実装 3333 def building_loop builder llvm_ir = nil loop do op, l, r = Fiber.yield llvm_ir case op when "+" llvm_ir = builder.add l, r when "-" llvm_ir = builder.sub l, r when '*' llvm_ir = builder.mul l, r when "/" llvm_ir = builder.sdiv l, r when :exit llvm_ir = builder.ret l break end end end def building_loop builder llvm_ir = nil loop do op, l, r = Fiber.yield llvm_ir case op when "+" llvm_ir = builder.add l, r when "-" llvm_ir = builder.sub l, r when '*' llvm_ir = builder.mul l, r when "/" llvm_ir = builder.sdiv l, r when :exit llvm_ir = builder.ret l break end end end Fiber から呼出し元への返り値みたいなもの。 Fiber なので、Fiber.yield の引数で処理結果を 渡して呼出し元に処理を戻す。 Fiber から呼出し元への返り値みたいなもの。 Fiber なので、Fiber.yield の引数で処理結果を 渡して呼出し元に処理を戻す。 呼出し元から来る引数みたいなもの。 Fiber なので、Fiber.yield の返り値で 呼出しから受け取る。 呼出し元から来る引数みたいなもの。 Fiber なので、Fiber.yield の返り値で 呼出しから受け取る。 四則演算の LLVM の命令を生成 四則演算の LLVM の命令を生成 計算結果を return する LLVM の命令を生成 計算結果を return する LLVM の命令を生成
  • 35. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の LLVMBuilder の実装 Fiber の処理を calc 、ret というメソッドで隠ぺい calc +、-、×、÷ の四則演算を LLVM の処理に変換する ret 処理結果を LLVM の関数の返り値にする。 3434 def int n LLVM.Int n.to_i end def calc op, l, r @fiber.resume op, l, r end def ret x @fiber.resume :exit, x end def dump @module.dump end def int n LLVM.Int n.to_i end def calc op, l, r @fiber.resume op, l, r end def ret x @fiber.resume :exit, x end def dump @module.dump end Fiber の外から、演算を受け取って、 Fiber に渡す。 さらに Fiber での処理結果を返す。 Fiber の外から、演算を受け取って、 Fiber に渡す。 さらに Fiber での処理結果を返す。 Fiber がまだ動いていたら、 :exit を送って、終了させる。 BasicBlock#build のブロックの処理が 終わらないと、正常に動作しない。 Fiber がまだ動いていたら、 :exit を送って、終了させる。 BasicBlock#build のブロックの処理が 終わらないと、正常に動作しない。 Ruby の整数を LLVM の整数に 変換する。 Ruby の整数を LLVM の整数に 変換する。 処理を終了し、返り値として返す処理を終了し、返り値として返す
  • 36. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 3535
  • 37. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 まとめ ( Parslet と Ruby-LLVM の夢の競演) Parslet とは Ruby 用の PEG パーサ生成ライブラリ 他の同種のライブラリと比較して 内部DSL の書き方がイケてる Ruby のふつうの Hash を抽象構文木に使うので扱いやすい 電卓の実装をしてみた。 作ってみたかんじ、案外カンタン Ruby-LLVM の Basic Block への命令の追加が、 ブロック内で処理する仕様になっている Parslet の DSL との相性がよくなかった Fiber を使い、うまく解決 3636
  • 38. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM の Pass について 3737
  • 39. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 第6章 Pass について Pass とは LLVM IR に対して解析/最適化操作などを行うもの Pass の種類 Immutable Pass LLVM IR の変換を行わない Pass。最適化のための静的情報を提供 Module Pass プログラム全体が対象の Pass。Function の追加/削除が可能。 Function Pass プログラム中のすべての Function に対して実行される Pass。 Loop Pass 各Function のすべての Loop に対して実行される Pass。 Region Pass 各Function のすべての Region に対して実行される Pass。 BasicBlockPass すべての BasicBlock(ジャンプのない一連の処理)に対して実行される Pass。 3838
  • 40. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 easypass.hpp FunctionPass の実装例として、EasyPass を紹介する runOnFunction 仮想メンバ関数が Function ごとに 実行される 3939 #include<cstdio> #include<llvm/Pass.h> #include<llvm/Function.h> using namespace llvm; class EasyPass : public FunctionPass{ public: static char ID; EasyPass() : FunctionPass(ID){} ~EasyPass(){} virtual bool runOnFunction(Function &F); }; #include<cstdio> #include<llvm/Pass.h> #include<llvm/Function.h> using namespace llvm; class EasyPass : public FunctionPass{ public: static char ID; EasyPass() : FunctionPass(ID){} ~EasyPass(){} virtual bool runOnFunction(Function &F); }; FunctionPass や Pass の定義があるFunctionPass や Pass の定義がある Function の定義があるFunction の定義がある おまじないおまじない Function ごとに実行 される仮想メンバ関数 Function ごとに実行 される仮想メンバ関数 EasyPass という FunctionPass を新たに定義 EasyPass という FunctionPass を新たに定義 llvm の名前空間を利用llvm の名前空間を利用
  • 41. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 easypass.cpp Function の名前を標準エラー出力に表示 テンプレートクラス RegisterPass のテンプレート引数 に EasyPass を渡してインスタンス化すると、 EasyPass が利用可能になる。 4040 #include "easypass.hpp" bool EasyPass::runOnFunction(Function &F){ fprintf(stderr, "Function Name : %s¥n", F.getName().str().c_str()); return false; } // char EasyPass::ID=0; static RegisterPass<EasyPass> X("easypass", "easy pass:only print function name", false , false); #include "easypass.hpp" bool EasyPass::runOnFunction(Function &F){ fprintf(stderr, "Function Name : %s¥n", F.getName().str().c_str()); return false; } // char EasyPass::ID=0; static RegisterPass<EasyPass> X("easypass", "easy pass:only print function name", false , false); 前のスライドの easypass.hpp を利用前のスライドの easypass.hpp を利用 関数名を表示関数名を表示 RegisterPass テンプートクラスを インスタンス化すると、EasyPass が 登録され、利用できるようになる。 第1引数: Pass の名前 第2引数: help への表示用文字列 第3引数: CFG を利用するが、 変更しない場合 true 第4引数: 解析用のパスなら true RegisterPass テンプートクラスを インスタンス化すると、EasyPass が 登録され、利用できるようになる。 第1引数: Pass の名前 第2引数: help への表示用文字列 第3引数: CFG を利用するが、 変更しない場合 true 第4引数: 解析用のパスなら true クラス変数 ID を 初期化 ※ 値はなんでもいいらしい クラス変数 ID を 初期化 ※ 値はなんでもいいらしい
  • 42. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 easypass の実行 4141 $ LLVM_DIR=/usr/lib/llvm-3.1 $ CXXFLAGS=`${LLVM_DIR}/bin/llvm-config --cxxflags` $ g++ -g ./src/easypass.cpp –I./inc ${CXXFLAGS} –c –o ./obj/easypass.o $ g++ -g –shared –o ./bin/easypass.so ./obj/easypass.o $ LLVM_DIR=/usr/lib/llvm-3.1 $ CXXFLAGS=`${LLVM_DIR}/bin/llvm-config --cxxflags` $ g++ -g ./src/easypass.cpp –I./inc ${CXXFLAGS} –c –o ./obj/easypass.o $ g++ -g –shared –o ./bin/easypass.so ./obj/easypass.o $ ${LLVM_DIR}/bin/opt –load ./bin/easypass.so –easypass ¥ –debug-pass=Structure ./sample/test.ll –S > /dev/null $ ${LLVM_DIR}/bin/opt –load ./bin/easypass.so –easypass ¥ –debug-pass=Structure ./sample/test.ll –S > /dev/null Pass Arguments: -targetlibinfo -easypass -preverify -domtree -verify -print-module Target Library Information ModulePass Manager FunctionPass Manager easy pass:only print function name Preliminary module verification Dominator Tree Construction Module Verifier Print module to stderr Function Name : test Function Name : main Pass Arguments: -targetlibinfo -easypass -preverify -domtree -verify -print-module Target Library Information ModulePass Manager FunctionPass Manager easy pass:only print function name Preliminary module verification Dominator Tree Construction Module Verifier Print module to stderr Function Name : test Function Name : main -shared オプションで、共有オブジェクトにする。-shared オプションで、共有オブジェクトにする。 -load で so をロードすると、-easypassが使える。-load で so をロードすると、-easypassが使える。 -debug-pass=Structure の表示-debug-pass=Structure の表示 easypass による出力easypass による出力 Function Pass として easypass が実行されるFunction Pass として easypass が実行される
  • 43. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 4242
  • 44. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 ModulePass ModulePass とは プログラム全体を対象とする Function 本体の参照、Function の追加/削除が可能 最適化処理、プログラムの解析などのために使われる 使い方 ModulePass を継承し、runOnModule をオーバーロード 最適化で、Module が修正された場合は true を返す。 ほかも同様に runOnXX というメソッドで目的の処理を行う。 4343 namespace llvm{ class ModulePass{ virtual bool runOnModule(Module &M) = 0; } } namespace llvm{ class ModulePass{ virtual bool runOnModule(Module &M) = 0; } }
  • 45. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 FunctionPass FunctionPass とは プログラムの中のすべての Function に対して実行される 一番よく使われる Pass 対象の Function 以外の Function の操作は不可 Module に Function の追加/削除不可 Module にグローバル変数の追加/削除不可 runOnFunction の実行にまたがった状態の維持 使い方 runOnFunction が Function ごとに呼び出される Function が修正された場合は true を返す。 4444 namespace llvm{ class FunctionPass{ virtual bool doInitialization(Module &M); virtual bool runOnFunction(Function &F) = 0; virtual bool doFinalization(Module &M); } } namespace llvm{ class FunctionPass{ virtual bool doInitialization(Module &M); virtual bool runOnFunction(Function &F) = 0; virtual bool doFinalization(Module &M); } }
  • 46. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LoopPass LoopPass とは 各Function のすべての Loop に対して実行される 多重ループの場合は内側から順に処理される LPPassManager を使うといろいろできる Loop のネスト構造の変更 Function や Module レベルの解析情報へのアクセス 使い方 runOnLoop が各 Function のすべての Loop で実行される 修正された場合は true を返す。 4545 namespace llvm{ class LoopPass{ virtual bool doInitialization(Loop *, LPPassManager &LPM); virtual bool runOnLoop(Loop *, LPPassManager &LPM) = 0; virtual bool doFinalization(); } } namespace llvm{ class LoopPass{ virtual bool doInitialization(Loop *, LPPassManager &LPM); virtual bool runOnLoop(Loop *, LPPassManager &LPM) = 0; virtual bool doFinalization(); } }
  • 47. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 RegionPass RegionPass とは LoopPass とよく似ている 出入り口を1つ持つ Region に対して実行される ネストの場合は内側から順に処理される RGPassManager を使うといろいろできる RegionTree の変更 Function や Module レベルの解析情報へのアクセス 使い方 runOnRegion が各 Function のすべての Loop で実行される 修正された場合は true を返す。 4646 namespace llvm{ class RegionPass{ virtual bool doInitialization(Region *R, RGPassManager &RGM); virtual bool runOnRegion(Region *R, RGPassManager &RGM) = 0; virtual bool doFinalization(); } } namespace llvm{ class RegionPass{ virtual bool doInitialization(Region *R, RGPassManager &RGM); virtual bool runOnRegion(Region *R, RGPassManager &RGM) = 0; virtual bool doFinalization(); } }
  • 48. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 BasicBlockPass BasicBlockPass とは FunctionPass とよく似ている 単一の BasicBlock を参照や変更の対象とする。 ※ 出入り口が1つでラベルがあり、ジャンプがない一連の処理 他の BasicBlock の参照や変更は禁止 使い方 runOnBasicBlock がすべての BasicBlock で実行される 修正された場合は true を返す。 4747 namespace llvm{ class BasicBlockPass{ virtual bool doInitialization(Function &F); virtual bool runOnBasicBlock(BasicBlock &BB) = 0; virtual bool doFinalization(Function &F); } } namespace llvm{ class BasicBlockPass{ virtual bool doInitialization(Function &F); virtual bool runOnBasicBlock(BasicBlock &BB) = 0; virtual bool doFinalization(Function &F); } }
  • 49. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Pass の実装に関してその他 Pass::getAnalysisUsage 他の Pass の実行結果を利用することができる 他の Pass の解析結果に影響がないことを伝達できる。 デフォルトでは、他の実行結果を利用しないし、 他の Pass の解析結果は無効化される AnalysisUsage::addRequired<> 実装する Pass の事前に実行したい Pass を登録できる AnalysisUsage::addRequiredTransive<> Pass 内で別の Pass に実行処理を遷移させることができる AnalysisUsage::addPreserved<> 影響のない Pass を指定し、必要のない再計算を回避できる。 Pass::getAnalysis<> 事前に addRequired で指定した Pass の実行結果を取得できる RegisterPass<> Pass を登録するときに使う。 4848
  • 50. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Pass の例 下記のコマンドで opt で使われている Pass を確認可能 4949 $ llvm-as < /dev/null | ¥ opt -std-compile-opts -disable-output -debug-pass=Arguments Pass Arguments: -targetlibinfo -no-aa -tbaa -basicaa -preverify - domtree -verify -globalopt -ipsccp -deadargelim -instcombine - simplifycfg -basiccg -prune-eh -inline -functionattrs -argpromotion -scalarrepl-ssa -domtree -early-cse -simplify-libcalls -lazy-value- info -jump-threading -correlated-propagation -simplifycfg - instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch - instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop- idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt - sccp -instcombine -lazy-value-info -jump-threading -correlated- propagation -domtree -memdep -dse -adce -simplifycfg -instcombine - strip-dead-prototypes -globaldce -constmerge -preverify -domtree - verify $ llvm-as < /dev/null | ¥ opt -std-compile-opts -disable-output -debug-pass=Arguments Pass Arguments: -targetlibinfo -no-aa -tbaa -basicaa -preverify - domtree -verify -globalopt -ipsccp -deadargelim -instcombine - simplifycfg -basiccg -prune-eh -inline -functionattrs -argpromotion -scalarrepl-ssa -domtree -early-cse -simplify-libcalls -lazy-value- info -jump-threading -correlated-propagation -simplifycfg - instcombine -tailcallelim -simplifycfg -reassociate -domtree -loops -loop-simplify -lcssa -loop-rotate -licm -lcssa -loop-unswitch - instcombine -scalar-evolution -loop-simplify -lcssa -indvars -loop- idiom -loop-deletion -loop-unroll -memdep -gvn -memdep -memcpyopt - sccp -instcombine -lazy-value-info -jump-threading -correlated- propagation -domtree -memdep -dse -adce -simplifycfg -instcombine - strip-dead-prototypes -globaldce -constmerge -preverify -domtree - verify
  • 51. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 主要な最適化 Pass の概要 概要 処理内容の例 -instcombine 複数の命令を統合 2回の +1 を +2 に変更 -simplifycfg Call Flow Graph を単純化 不要な Basic Block の削除。 前後の Basic Block をマージ。 -inline 関数のインライン化 caller に関数の内容をマージ -jump-threading 特定の分岐に必ず遷移すると きに、ジャンプの数を減らす if(true){ X=4; } if( X < 3 ){ … } で、ジャンプの数を減らす。 -indvars ループ変数を正規化 for(i=7; i*I < 1000; ++i) を for(i=0; i < 25; ++i) に変換 -licm ループ内で不変の計算をルー プ外に移動 for( … ){ x = y + z; … } を x = y + z; for( … ){ … } に変換 -loop-unswitch ループ内の条件分岐を条件分 岐内のループに変換 for( … ){ A; if(test) B; C} を if(test){for(…) {A; B; C;}} else{for(…) {A; C}} に変換 -loop-unroll ループの終了判定の負担を減 らすため、ループ展開を行う。 http://ja.wikipedia.org/wiki/ ループ展開 5050
  • 52. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 instcombine の例 1 + 1 の計算がなくなり、直接 2 を返すようになっている 5151 int two(void){ int one = 1; return one + 1; } int two(void){ int one = 1; return one + 1; } ; (snip) define i32 @two() nounwind { %one = alloca i32, align 4 store i32 1, i32* %one, align 4 %1 = load i32* %one, align 4 %2 = add nsw i32 %1, 1 ret i32 %2 } ; (snip) define i32 @two() nounwind { %one = alloca i32, align 4 store i32 1, i32* %one, align 4 %1 = load i32* %one, align 4 %2 = add nsw i32 %1, 1 ret i32 %2 }; (snip) define i32 @two() nounwind { ret i32 2 } ; (snip) define i32 @two() nounwind { ret i32 2 } clang –cc1 instcombine.c –emit-llvmclang –cc1 instcombine.c –emit-llvm opt –instcombine instcombine.ll | llvm-disopt –instcombine instcombine.ll | llvm-dis
  • 53. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 inline の例 最適化により、関数 one() の呼出しがなくなり、さらに その定義もなくなっている。 5252 static int one(void){ return 1; } int two(void){ return one() + 1; } static int one(void){ return 1; } int two(void){ return one() + 1; } ModuleID = 'inline.c' target datalayout = "(snip)" target triple = "x86_64-pc-linux-gnu" define i32 @two() nounwind { %1 = call i32 @one() %2 = add nsw i32 %1, 1 ret i32 %2 } define internal i32 @one() nounwind { ret i32 1 } ModuleID = 'inline.c' target datalayout = "(snip)" target triple = "x86_64-pc-linux-gnu" define i32 @two() nounwind { %1 = call i32 @one() %2 = add nsw i32 %1, 1 ret i32 %2 } define internal i32 @one() nounwind { ret i32 1 } ; (snip) define i32 @two() nounwind { %1 = add nsw i32 1, 1 ret i32 %1 } ; (snip) define i32 @two() nounwind { %1 = add nsw i32 1, 1 ret i32 %1 } opt –inline inline.ll | llvm-disopt –inline inline.ll | llvm-dis
  • 54. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 simplifycfg の例 return 2 の処理が共通なので、Basic Block がマージ されている。 5353 int two(void){ int one = 1; if(one) return 2; else return 2; } int two(void){ int one = 1; if(one) return 2; else return 2; } define i32 @two() nounwind { %1 = alloca i32, align 4 %one = alloca i32, align 4 store i32 1, i32* %one, align 4 %2 = load i32* %one, align 4 %3 = icmp ne i32 %2, 0 br i1 %3, label %4, label %5 ; <label>:4 ; preds = %0 store i32 2, i32* %1 br label %6 ; <label>:5 ; preds = %0 store i32 2, i32* %1 br label %6 ; <label>:6 ; preds = %5, %4 %7 = load i32* %1 ret i32 %7 } define i32 @two() nounwind { %1 = alloca i32, align 4 %one = alloca i32, align 4 store i32 1, i32* %one, align 4 %2 = load i32* %one, align 4 %3 = icmp ne i32 %2, 0 br i1 %3, label %4, label %5 ; <label>:4 ; preds = %0 store i32 2, i32* %1 br label %6 ; <label>:5 ; preds = %0 store i32 2, i32* %1 br label %6 ; <label>:6 ; preds = %5, %4 %7 = load i32* %1 ret i32 %7 } define i32 @two() nounwind { %1 = alloca i32, align 4 %one = alloca i32, align 4 store i32 1, i32* %one, align 4 store i32 2, i32* %1 %2 = load i32* %1 ret i32 %2 } define i32 @two() nounwind { %1 = alloca i32, align 4 %one = alloca i32, align 4 store i32 1, i32* %one, align 4 store i32 2, i32* %1 %2 = load i32* %1 ret i32 %2 }
  • 55. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 まとめ(LLVM の Pass について) LLVM の Pass で最適化、コードの解析が可能 言語、アーキテクチャから独立な最適化を実施 独自の Pass の実装も簡単 今回は紹介しなかったが、下記の機能もある Call Flow Graph を dot でグラフ化することや 命令の種類ごとに、数をカウント さらに LLVM の主要な最適化 Pass を紹介 instcombine : 複数の命令を1つにマージ inline : みんな大好き関数のインライン化 simplifycfg : 複数の Basic Block のマージとか あと、ループの展開、正規化、ループ内で不変の計算の外出し、 条件分岐とのネスト順の変更とかもある 5454