Slide 1

Slide 1 text

Rust でLLVM IR 入門 @rchaser53

Slide 2

Slide 2 text

誰 ? 誰 ? 職業: 雑用 web エンジニア 名前: 吉澤 峻行 最近はVue とかGo とか

Slide 3

Slide 3 text

を用いてhello world する話 思いの外hello world で苦戦したので書いた プリントデバッグに必要な知識だから仕方ないね LLVM IR をこれから触る方の足しになれば幸いです llvm-sys.rs

Slide 4

Slide 4 text

前座が長いので本題だけ見たい方は 前座が長いので本題だけ見たい方は 14 ページまでジャンプしていただければ… サンプルの loop, if, struct の使用などのサンプルもある( 暇なら) レポジトリ

Slide 5

Slide 5 text

LLVM とは ? LLVM とは ? コンパイラ基盤を作るプロジェクト ツールチェーンやコンパイラ、モジュールなどを提供 Rust もLLVM を使用している

Slide 6

Slide 6 text

LLVM IR とは ? LLVM IR とは ? LLVM はThree-stage compiler structure を採用している LLVM IR は各フェイズ間のデータのやりとりに使う

Slide 7

Slide 7 text

THREE-STAGE COMPILER STRUCTURE THREE-STAGE COMPILER STRUCTURE 複数のフェイズに分けて開発するコンパイラの設計方法 フロントエンド パース, エラーチェック、AST の生成など ミドルエンド(Optimizer) 最適化、高速化など バックエンド 各プラットフォーム向けの最終産物の生成など IR を用いて各フェイズでデータのやり取りをする

Slide 8

Slide 8 text

LLVM IR とは ? LLVM IR とは ? こんな感じで各フェイズ間のデータのやりとりに使う 以下でRust でも見れる cargo rustc -- --emit=llvm-ir

Slide 9

Slide 9 text

背景 背景 をRust で書いてる じゃあ改良して任意のLLVM IR を出力してみよう Go 言語でつくるインタプリタ 制作途中のものはこれ もうやった人いる

Slide 10

Slide 10 text

背景 背景 hello world までの道のりが辛い 資料らしい資料がない では自分で書こう( イマココ)

Slide 11

Slide 11 text

何を使うの ? 何を使うの ? というLLVM IR を出力するモジュールを使う LLVM はLLVM IR を出力するモジュールを提供している C とC++ で書かれておりllvm-sys.rs はC のやつをbinding している llvm-sys.rs

Slide 12

Slide 12 text

LLVM-SYS.RS LLVM-SYS.RS 非常に薄い。ほとんどC のAPI と大差ない ドキュメントやサンプルはほぼない C のAPI に関しても少ない。C++ ならいくらか 困ったら を見に行くのをオススメ ponyc

Slide 13

Slide 13 text

何が難しい ? 何が難しい ? GetElementPtr(GEP) が難しいと思う まである LLVM IR 上で直にいじるのにも慣れがいる C のAPI やリファレンスが正直わかりにくいのも原因 公式の専用の解説ページ

Slide 14

Slide 14 text

GEP(GETELEMENTPTR) とは GEP(GETELEMENTPTR) とは Array やStruct のsubelement のアドレスを取得する つまりArray やStruct を操作する際に把握が必須 文字列はi8 のArray 。hello world するにはこいつが必要

Slide 15

Slide 15 text

まずは LLVM IR で出力できるようにする まずは LLVM IR で出力できるようにする 拡張子はll 。test.ll とか作れば良い LLVM のツールチェーンであるlli を用いて実行すると楽 $ lli test.ll

Slide 16

Slide 16 text

多分の最小の構成 define i32 @main(i32, i8**) { entry: %local_str = alloca [12 x i8] store [12 x i8] c"hello world\00", [12 x i8]* %local_str %input_puts = getelementptr [12 x i8], [12 x i8]* %local_str, i32 0 call i32 @printf(i8* %input_puts) ret i32 0 } declare i32 @printf(i8*)

Slide 17

Slide 17 text

解説1 ; 頭文字 ; 行 ; @xxx 変数 %yyy 変数 型 ; 戻 値 型 関数名(引数 型1, 引数 型2) define i32 @main(i32, i8**) { ; 必須 entry: %local_str = alloca [12 x i8] store [12 x i8] c"hello world\00", [12 x i8]* %local_str %input_puts = getelementptr [12 x i8], [12 x i8]* %local_str, i32 0 call i32 @printf(i8* %input_puts) ret i32 0 }

Slide 18

Slide 18 text

解説2 define i32 @main(i32, i8**) { entry: ; 確保 ; [12 x i8] 要素 12 Array %local_str = alloca [12 x i8] ; 確保 値 設定 ; 基本的 型 値 形 記述 ; (例) [12 x i8] c"hello world\00" store [12 x i8] c"hello world\00", [12 x i8]* %local_str ; getelementptr(後述) %input_puts = getelementptr [12 x i8], [12 x i8]* %local_str, i32 0 call i32 @printf(i8* %input_puts) i

Slide 19

Slide 19 text

解説3 define i32 @main(i32, i8**) { entry: %local_str = alloca [12 x i8] store [12 x i8] c"hello world\00", [12 x i8]* %local_str %input_puts = getelementptr [12 x i8], [12 x i8]* %local_str, i32 0 ; 関数呼 出 call i32 @printf(i8* %input_puts) ; return ret i32 0 } ; 標準 呼 出 関数 指定 declare i32 @printf(i8*)

Slide 20

Slide 20 text

LLVM IR の GEP LLVM IR の GEP Array やStruct のsubelement のアドレスを取得する 引数のパターンはいくらかある llvm-sys.rs から出力できないパターンは割愛 ; 型, 型 変数, index1, index2, ...(以下状況 変更) getelementptr %[12 x i8], %[12 x i8]* %local_str, i32 0, i32 0 型, 型のポインタはわかるけど、index は何? => Array やStruct で考えてみる

Slide 21

Slide 21 text

LLVM IR の GEP(ARRAY) LLVM IR の GEP(ARRAY) // 感 Array 値 取得 int temp_array[4][4]; temp_array[1][3]; ; 上記 型 宣言 %array_type = type [4 x [4 x i8]] define i8* @test_array_gep(%array_type* %a) { entry: %ret_val = getelementptr %array_type, %array_type* %a, i32 0, i ret i8* %ret_val }

Slide 22

Slide 22 text

LLVM IR の GEP(STRUCT) LLVM IR の GEP(STRUCT) // 感 Struct 値 取得 struct temp_struct { char a; int b int c[4]; }; temp_struct.b ; 上記 型 宣言 %struct_type = type { i8, i32, [4 x i8] } define i32* @test_stuct_gep(%struct_type* %s) { entry: ; 直接field名 指定 肩 宣言 順番 記述 ; b 2 目 宣言 1 %ret_val = getelementptr %struct_type, %struct_type* %s, i32 0, ret i32* %ret_val }

Slide 23

Slide 23 text

LLVM IR の GEP LLVM IR の GEP ; 型, 型 変数, index1, index2, ...(以下状況 変更) getelementptr %[12 x i8], %[12 x i8]* %local_str, i32 0, i32 0 常にindex1 が0 にならない…?

Slide 24

Slide 24 text

LLVM IR の GEP LLVM IR の GEP index1 が0 以外のケース。ないことはないらしい… // C struct munger_struct { int f1; int f2; }; void munge(struct munger_struct *P) { P[0].f1 = P[1].f1 + P[2].f2; } ; LLVM IR void %munge(%struct.munger_struct* %P) { entry: %tmp = getelementptr %struct.munger_struct, %struct.munger_struct %tmp = load i32* %tmp %tmp6 = getelementptr %struct.munger_struct, %struct.munger_struc %tmp7 = load i32* %tmp6 %tmp8 = add i32 %tmp7, %tmp %tmp9 = getelementptr %struct.munger_struct, %struct.munger_struc store i32 %tmp8, i32* %tmp9 ret void }

Slide 25

Slide 25 text

LLVM-SYS.RS LLVM-SYS.RS 基本はllvm-sys.rs のexample を参考にすれば良い unsafe とか生ポインタとかの話は割愛 LLVMBuildGEP についての説明をする

Slide 26

Slide 26 text

作成する LLVM IR 作成する LLVM IR %local_str = alloca [12 x i8] store [12 x i8] c"hello world\00", [12 x i8]* %local_str %input_puts = getelementptr [12 x i8], [12 x i8]* %local_str, i32 0,

Slide 27

Slide 27 text

以下Rust のコード 長いし読みにくいので実際のコードの 貼る リンク先のコードの方がいくらかマシだと思う 次のスライドでgetelementptr だけ説明する リンク pub fn codegen_string( builder: *mut LLVMBuilder, context: *mut LLVMContext, input_str: &str, ) -> *mut LLVMValue { let length = input_str.len() as u32; unsafe { // %local_str = alloca [12 x i8] let llvm_value = LLVMBuildAlloca( builder, LLVMArrayType(LLVMInt8Type(), length), c_string!("").as_ptr(), ); // i \ i

Slide 28

Slide 28 text

お作法なので知ってしまえば簡単 お作法なので知ってしまえば簡単 // getelementptr [12 x i8], [12 x i8]* %local_str, i32 0, i32 0 // i32 0, i32 0 let mut args = [ LLVMConstInt(LLVMInt32Type(), 0, 0), LLVMConstInt(LLVMInt32Type(), 0, 0), ]; return LLVMBuildGEP( builder, llvm_value, // Array Struct args.as_mut_ptr(), args.len() as u32, // 第3引数 length 引数 指定 意味 CString::new("").unwrap().as_ptr(), // 空文字 場合 勝手 変数名 生成 );

Slide 29

Slide 29 text

まとめ 1 まとめ 1 GETELEMENTPTR について GETELEMENTPTR について 慣れればそこまで難しいものではないと思う 使えないとほぼ何もできないので勉強できて良かった C のAPI 使われてなさすぎな気がするのだけど何故?

Slide 30

Slide 30 text

まとめ 2 まとめ 2 LLVM-SYS.RS で LLVM IR を出力する意味はあるのか ? LLVM-SYS.RS で LLVM IR を出力する意味はあるのか ? あまりなさそう。Rust でLLVM IR を出力すれば良さそう な気はする しかしLLVM IR について知れたのは良いことなのでは? まぁその時楽しめれば良いよね

Slide 31

Slide 31 text

参考リンク 公式reference 公式のGetElementPtr の詳細な解説 ponyc のレポジトリ( 困ったら読む) The Architecture of Open Source Applications のLLVM Three-stage_compiler_structure きつねさんでもわかるLLVM

Slide 32

Slide 32 text

ご静聴ありがとうございました ! ご静聴ありがとうございました !