きつねさんでもわかるLlvm読書会 第2回
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

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

  • 4,935 views
Uploaded on

参考: ...

参考:
 https://github.com/ruby-llvm/ruby-llvm/tree/master/samples
 https://github.com/Kmotiko/DummyCCompiler
 http://kschiess.github.io/parslet/
 https://github.com/ruby-llvm/ruby-llvm
 
https://github.com/cuzic/llvm-kitsunesan

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
4,935
On Slideshare
4,813
From Embeds
122
Number of Embeds
1

Actions

Shares
Downloads
26
Comments
0
Likes
6

Embeds 122

https://twitter.com 122

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. きつねさんでもわかる LLVM読書会 第2回 2013/7/6 cuzic
  • 2. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 自己紹介 最近のできごと Ruby勉強会で TRICK に関する発表をしました。 TRICK: 超絶技巧Ruby意味不明コンテスト IOCCC の Ruby版 やっつけで資料を作ったわりにはみなさんから 好評だったようでよかったです いろんな活動を始めました 山登り クーリエジャポン朝食会 次回の勉強会 次回の amagasakirb は 8月10日(土)で考えています。 Land of Lisp の読書会の予定です。 1
  • 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 について紹介 2
  • 4. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM概要と Ruby-LLVM 3
  • 5. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM とは LLVM とは コンパイラを作成するための共通基盤 BSDライセンス で C++ で開発 多くのサブプロジェクトから構成される LLVM Core、Clang、 etc LLVM Core とは 言語やアーキテクチャから独立した中間表現 LLVM IR を規定 新言語の開発で字句解析や構文解析だけを作成し、他は流用できる 最適化や新たなアーキテクチャ対応の試作なども可能 さまざまな最適化、多くのアーキテクチャへのコード生成に対応 LLVM IR のレベルで最適化するため、言語やアーキテクチャから独立 C++ 等の言語へのフロントエンド実装 Clang が有名 LLVM をコンパイラ基盤ではなくコンパイラとして利用可能 4
  • 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 が必要 5 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 インストール方法 6 ### 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 7 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); }
  • 9. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 (復習) Module, Function, Basic Block, Instruction 8 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 四則演算、ビット演算、型変換、条件分岐、メモリアクセスなど 処理の最小単位 9
  • 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 を実行する。 10
  • 12. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM で Hello, World Hello, World を出力する例を Ruby-LLVM で再実装する。 http://llvm.org/docs/LangRef.html#module-structure 11 ; 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 実装例 12 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 読書会用資料 デモ 13
  • 15. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby-LLVM での Hello, World (1) おまじない 変数 m が新しく生成する LLVM の Module 14 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!" で初期化する。 15 # 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 オブジェクト 16 # 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: 複雑なデータ構造での格納場所(アドレス)を取得する。 17 # 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 を出力可能 標準エラー出力で出力される 18 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 関数を実行する。 19 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 実装例(再掲) 20 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 だいたい再現できている。 21 ; 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 経由かで値を変えられる 22 ; 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); }
  • 24. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Ruby LLVM での階乗計算の実装例 23 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引数でどこから来れば、どんな値にするかハッシュで渡す。 24 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 読書会用資料 デモ 25
  • 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引数の順番が違う 26
  • 28. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet と Ruby-LLVM の 夢の競演 27
  • 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 のハッシュで表現されている 28
  • 30. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet で電卓を実装してみる 仕様 計算できるのは、Int だけ 計算できる演算は +、-、*、/ の4つだけ 答えももちろん Int だけ *、/ は +、- より優先順位が高い 出力、計算 Ruby-LLVM で LLVM のコードを出力させる 実際の計算はしないつもり 工夫している点 BasicBlock#build の引数のブロック内で、 LLVM の命令を追加していくことが必要。 この仕様は Transformer の rule と相性ワルし! BasicBlock#build を別の Fiber で実行することで解決! 29
  • 31. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の Parser の実装 30 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 を記述するだけで比較的簡単に実装可能 31 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 の整数に変換。 パーサによって生成された "left"、 "right"、 "op" の 要素を持つサブツリーに対して 処理を行う。 LLVM の命令に変換する。
  • 33. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の LLVMBuilder の実装 LLVMBuilder では LLVM の命令を生成する。 building_loop で rule からの指示を待つ 32 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 を生成している。 1. Module の生成 2. Function の追加 3. Basic Block の追加 4. Builder を使い、LLVM 命令を追加 building_loop まで Fiber を実行させておく。
  • 34. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の LLVMBuilder の実装 33 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.yield の返り値で 呼出しから受け取る。 四則演算の LLVM の命令を生成 計算結果を return する LLVM の命令を生成
  • 35. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 電卓の LLVMBuilder の実装 Fiber の処理を calc 、ret というメソッドで隠ぺい calc +、-、×、÷ の四則演算を LLVM の処理に変換する ret 処理結果を LLVM の関数の返り値にする。 34 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 での処理結果を返す。 生成された LLVM の計算プログラムを 出力する。 Ruby の整数を LLVM の整数に 変換する。 処理を終了し、演算結果を返り値 として返す
  • 36. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 35
  • 37. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 まとめ ( Parslet と Ruby-LLVM の夢の競演) Parslet とは Ruby 用の PEG パーサ生成ライブラリ 他の同種のライブラリと比較して 内部DSL の書き方がイケてる Ruby のふつうの Hash を抽象構文木に使うので扱いやすい 電卓の実装をしてみた。 作ってみたかんじ、案外カンタン Ruby-LLVM の Basic Block への命令の追加が、 ブロック内で処理する仕様になっている Parslet の DSL との相性がよくなかった Fiber を使い、うまく解決 36
  • 38. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM の Pass について 37
  • 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。 38
  • 40. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 easypass.hpp FunctionPass の実装例として、EasyPass を紹介する runOnFunction 仮想メンバ関数が Function ごとに 実行される 39 #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 の定義がある Function の定義がある おまじない Function ごとに実行 される仮想メンバ関数 EasyPass という FunctionPass を新たに定義 llvm の名前空間を利用
  • 41. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 easypass.cpp Function の名前を標準エラー出力に表示 テンプレートクラス RegisterPass のテンプレート引数 に EasyPass を渡してインスタンス化すると、 EasyPass が利用可能になる。 40 #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 を利用 関数名を表示 RegisterPass テンプートクラスを インスタンス化すると、EasyPass が 登録され、利用できるようになる。 第1引数: Pass の名前 第2引数: help への表示用文字列 第3引数: CFG を利用するが、 変更しない場合 true 第4引数: 解析用のパスなら true クラス変数 ID を 初期化 ※ 値はなんでもいいらしい
  • 42. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 easypass の実行 41 $ 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 オプションで、共有オブジェクトにする。 -load で so をロードすると、-easypassが使える。 -debug-pass=Structure の表示 easypass による出力 Function Pass として easypass が実行される
  • 43. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 42
  • 44. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 ModulePass ModulePass とは プログラム全体を対象とする Function 本体の参照、Function の追加/削除が可能 最適化処理、プログラムの解析などのために使われる 使い方 ModulePass を継承し、runOnModule をオーバーロード 最適化で、Module が修正された場合は true を返す。 ほかも同様に runOnXX というメソッドで目的の処理を行う。 43 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 を返す。 44 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 を返す。 45 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 を返す。 46 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 を返す。 47 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 を登録するときに使う。 48
  • 50. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Pass の例 下記のコマンドで opt で使われている Pass を確認可能 49 $ 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/ ループ展開 50
  • 52. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 instcombine の例 1 + 1 の計算がなくなり、直接 2 を返すようになっている 51 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-llvm opt –instcombine instcombine.ll | llvm-dis
  • 53. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 inline の例 最適化により、関数 one() の呼出しがなくなり、さらに その定義もなくなっている。 52 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-dis
  • 54. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 simplifycfg の例 return 2 の処理が共通なので、Basic Block がマージ されている。 53 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 のマージとか あと、ループの展開、正規化、ループ内で不変の計算の外出し、 条件分岐とのネスト順の変更とかもある 54
  • 56. 55 ご清聴ありがとう ございました