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

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

on

  • 4,291 views

参考: ...

参考:
 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

Statistics

Views

Total Views
4,291
Slideshare-icon Views on SlideShare
4,175
Embed Views
116

Actions

Likes
6
Downloads
24
Comments
0

1 Embed 116

https://twitter.com 116

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

    • きつねさんでもわかる LLVM読書会 第2回 2013/7/6 cuzic
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 自己紹介 最近のできごと Ruby勉強会で TRICK に関する発表をしました。 TRICK: 超絶技巧Ruby意味不明コンテスト IOCCC の Ruby版 やっつけで資料を作ったわりにはみなさんから 好評だったようでよかったです いろんな活動を始めました 山登り クーリエジャポン朝食会 次回の勉強会 次回の amagasakirb は 8月10日(土)で考えています。 Land of Lisp の読書会の予定です。 1
    • 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
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM概要と Ruby-LLVM 3
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM とは LLVM とは コンパイラを作成するための共通基盤 BSDライセンス で C++ で開発 多くのサブプロジェクトから構成される LLVM Core、Clang、 etc LLVM Core とは 言語やアーキテクチャから独立した中間表現 LLVM IR を規定 新言語の開発で字句解析や構文解析だけを作成し、他は流用できる 最適化や新たなアーキテクチャ対応の試作なども可能 さまざまな最適化、多くのアーキテクチャへのコード生成に対応 LLVM IR のレベルで最適化するため、言語やアーキテクチャから独立 C++ 等の言語へのフロントエンド実装 Clang が有名 LLVM をコンパイラ基盤ではなくコンパイラとして利用可能 4
    • 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 …
    • 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
    • 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); }
    • 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
    • 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
    • 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
    • 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}
    • 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"], *[])
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 13
    • 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')
    • 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)
    • 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);
    • 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
    • 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
    • 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"], *[])
    • 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"], *[])
    • 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!
    • 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); }
    • 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
    • 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")
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 25
    • 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
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet と Ruby-LLVM の 夢の競演 27
    • 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
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 Parslet で電卓を実装してみる 仕様 計算できるのは、Int だけ 計算できる演算は +、-、*、/ の4つだけ 答えももちろん Int だけ *、/ は +、- より優先順位が高い 出力、計算 Ruby-LLVM で LLVM のコードを出力させる 実際の計算はしないつもり 工夫している点 BasicBlock#build の引数のブロック内で、 LLVM の命令を追加していくことが必要。 この仕様は Transformer の rule と相性ワルし! BasicBlock#build を別の Fiber で実行することで解決! 29
    • 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
    • 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 の命令に変換する。
    • 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 を実行させておく。
    • 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 の命令を生成
    • 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 の整数に 変換する。 処理を終了し、演算結果を返り値 として返す
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 35
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 まとめ ( Parslet と Ruby-LLVM の夢の競演) Parslet とは Ruby 用の PEG パーサ生成ライブラリ 他の同種のライブラリと比較して 内部DSL の書き方がイケてる Ruby のふつうの Hash を抽象構文木に使うので扱いやすい 電卓の実装をしてみた。 作ってみたかんじ、案外カンタン Ruby-LLVM の Basic Block への命令の追加が、 ブロック内で処理する仕様になっている Parslet の DSL との相性がよくなかった Fiber を使い、うまく解決 36
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 LLVM の Pass について 37
    • 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
    • 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 の名前空間を利用
    • 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 を 初期化 ※ 値はなんでもいいらしい
    • 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 が実行される
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 デモ 42
    • 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; } }
    • 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); } }
    • 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(); } }
    • 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(); } }
    • 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); } }
    • 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
    • 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
    • 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
    • 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
    • 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
    • 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 }
    • 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料 まとめ(LLVM の Pass について) LLVM の Pass で最適化、コードの解析が可能 言語、アーキテクチャから独立な最適化を実施 独自の Pass の実装も簡単 今回は紹介しなかったが、下記の機能もある Call Flow Graph を dot でグラフ化することや 命令の種類ごとに、数をカウント さらに LLVM の主要な最適化 Pass を紹介 instcombine : 複数の命令を1つにマージ inline : みんな大好き関数のインライン化 simplifycfg : 複数の Basic Block のマージとか あと、ループの展開、正規化、ループ内で不変の計算の外出し、 条件分岐とのネスト順の変更とかもある 54
    • 55 ご清聴ありがとう ございました