Slide 1

Slide 1 text

1 構文追加で学ぶGoコンパイラの処理 Recap: Exploring the Go Compiler: Adding a "four" loop Go 1.23 リリースパーティ& GopherCon 2024 Recap @task4233

Slide 2

Slide 2 text

2 所属 ・Mercariのバックエンドエンジニア ・IDPチームで認証認可基盤まわりの開発をしています 興味 ・バックエンドとWebセキュリティ ・GopherCon 2024のCTFで個人賞を5つ貰いました✌ 自己紹介: @task4233

Slide 3

Slide 3 text

3 目的 ・Goコンパイラが機械語命令を生成するまでの処理を  ざっくりと理解してもらうこと 想定対象者 ・Goコンパイラの処理に興味がある人 ・cmd/compile/internal以下の処理に興味がある人 発表の目的と想定対象者

Slide 4

Slide 4 text

4 Recap対象セッションの概要 おしながき コンパイラ処理の概要 フロント / ミドル / バックエンドの変更と処理 解析時に便利なツール 02 03 04 01 まとめ 05

Slide 5

Slide 5 text

5 Exploring the Go Compiler: Adding a "four" loop ・2つの構文追加の実例に沿って、Goコンパイラの処理を紹介した発表 1. Recap対象のセッション概要 1: four statement 2: unless statement // Post stmtを4回繰り返すループ four i:=0; i<16; i++ { fmt.Println(i) } // 以下のforループと等価 for i:=0; i<16; i+=4 { fmt.Println(i) } // if notのシンタックスシュガー unless i%2==0 { fmt.Printf("odd number!") } // 以下のif文と等価 if !(i%2==0) { fmt.Printf("odd number!") }

Slide 6

Slide 6 text

6 Exploring the Go Compiler: Adding a "four" loop ・2つの構文追加の実例に沿って、Goコンパイラの処理を紹介した発表 1. Recap対象のセッション概要 1: four statement 2: unless statement // Post stmtを4回繰り返すループ four i:=0; i<16; i++ { fmt.Println(i) } // 以下のforループと等価 for i:=0; i<16; i+=4 { fmt.Println(i) } // if notのシンタックスシュガー unless i%2==0 { fmt.Printf("odd number!") } // 以下のif文と等価 if !(i%2==0) { fmt.Printf("odd number!") } 今回こちらのみ

Slide 7

Slide 7 text

7 コンパイラ処理は次の3ステージに大別される ・フロントエンド : ソースコードを解析して中間表現(IR)を構築するステージ ・ミドルエンド  : IRを最適化するステージ ・バックエンド  : 最適化されたIRを機械語へ変換するステージ 2. コンパイラ処理の概要 1. 字句解析 2. 構文解析 3. 型チェック 4. IRの構築 1. ASTに対する最適化 2. ASTの巡回 1. SSA形式の構築 2. 機械語生成 フロントエンド ミドルエンド バックエンド

Slide 8

Slide 8 text

8 1. 字句解析 2. 構文解析 3. 型チェック 4. IRの構築 1. ASTに対する最適化 2. ASTの巡回 1. SSA形式の構築 2. 機械語生成 フロントエンド ミドルエンド バックエンド

Slide 9

Slide 9 text

9 ソースコードをトークンと呼ばれる意味のある単位に分割する処理 ・トークン定義はsyntax/tokens.go に   3-1-1: 字句解析(Lexical Analysis) 出典: [1] tenntenn (2017) Go静的解析ハンズオン . 式v+1を字句解析した例[1]

Slide 10

Slide 10 text

10 トークンを用いて構文木を生成する処理   3-1-2: 構文解析(Parsing) 出典: [2] tenntenn (2017) Go静的解析ハンズオン . 式v+1を字句解析+構文解析した例[2]

Slide 11

Slide 11 text

11 ・既存のif statementの構文を踏襲 3-1: unless statementの構文 if ; { } unless ; { } unless statement if statement unless statementの struct(ref)

Slide 12

Slide 12 text

12 3-1: unless statementの解析部分 unless ; { } Header part (init, cond) ref

Slide 13

Slide 13 text

13 3-1: unless statementの解析部分 unless ; { } Block statement part (then) ref

Slide 14

Slide 14 text

14 3-1: unless statementの構文木 unless i%2==0 { fmt.Printf("odd number!") } 生成された構文木の抜粋

Slide 15

Slide 15 text

15 3-1: unless statementの構文木 unless i%2==0 { fmt.Printf("odd number!") } 生成された構文木の抜粋

Slide 16

Slide 16 text

16 構文木から型に関する情報を取得する処理 (ref) ・Name Resolution:プログラムの識別子をシンボルにマッピングする処理 ・Constant Folding:定数式の定数値を計算する処理 ・Type Deduction :式の型が言語仕様に準拠しているか確認する処理 3-1-3: 型チェック (Type Checking) 出典: [3] tenntenn (2017) Go静的解析ハンズオン . 型チェックの例[3]

Slide 17

Slide 17 text

17 3-1-3: unless statementの型チェック statementのチェック ref

Slide 18

Slide 18 text

18 3-1-3: unless statementの型チェック conditionのチェック ref

Slide 19

Slide 19 text

19 型チェック済みの具体的な構文木から抽象構文木 (AST)へ変換する処理 ・Intermediate Representation(IR)は最適化や変換に適した表現のこと ・Goでは、このプロセスを "noding" と呼ぶ 3-1-4: 中間表現(IR)の生成(Noding) unless statementのstruct(ref)

Slide 20

Slide 20 text

20 3-1-4: unless statementの中間表現 (IR)の生成

Slide 21

Slide 21 text

21 1. 字句解析 2. 構文解析 3. 型チェック 4. IRの構築 1. ASTに対する最適化 2. ASTの巡回 1. SSA形式の構築 2. 機械語生成 フロントエンド ミドルエンド バックエンド

Slide 22

Slide 22 text

22 3-2-1: ASTに対する最適化 最適化手法名 説明 デッドコード削除 呼ばれていない処理を削除する 仮想関数の実体化 (Devirtualization) インタフェースのメソッド呼び出しを structのメソッド呼び出しに 置き換える 関数のインライン化 (Inlining) 関数呼び出しのコードを呼び出し元に展開することで、 関数呼び出しのオーバーヘッドを高速化する エスケープ解析 プログラムの実行時にオブジェクトがどこで対比されるか、 どのスコープやスレッドの外部で利用されるかを分析する技術

Slide 23

Slide 23 text

23 Order ・複雑なstatementをより単純なものにして変換し、  評価の順序に気を遣うステップ Desugar ・高レベルの構造をプリミティブなものに変換するステップ ・例) unless statementを否定されたif statementにする 3-2-2: ASTの巡回

Slide 24

Slide 24 text

24 3-2-2: Unless StatementにおけるDesugarの例

Slide 25

Slide 25 text

25 3-2-2: Unless StatementにおけるDesugarの例 NOTで条件を逆に

Slide 26

Slide 26 text

26 3-2-2: Unless StatementにおけるDesugarの例 IfのStmtを再利用

Slide 27

Slide 27 text

27 1. 字句解析 2. 構文解析 3. 型チェック 4. IRの構築 1. ASTに対する最適化 2. ASTの巡回 1. SSA形式の構築 2. 機械語生成 フロントエンド ミドルエンド バックエンド

Slide 28

Slide 28 text

28 プログラムがブロックに分割されて各変数が一度だけ割り当てられる形式 3-3-1: SSA(Static Single-Assignment)形式 x := 1 x := 2 y := x x_1 := 1 x_2 := 2 y_1 := x_2 通常のソースコード SSAが適用されたコード

Slide 29

Slide 29 text

29 プログラムがブロックに分割されて各変数が一度だけ割り当てられる形式 3-3-1: SSA(Static Single-Assignment)形式 x := 1 x := 2 y := x x_1 := 1 x_2 := 2 y_1 := x_2 通常のソースコード SSAが適用されたコード デッドコード

Slide 30

Slide 30 text

30 3-3-1: 構文木 vs AST vs SSA形式 概要 強み 構文木 ・構文解析(Parsing)後に得られる木 ・あらゆるトークンが反映される ・文法の検証やソースコードの  再構築に役立つ 抽象構文木 (AST) ・IR構築(Noding)後に得られる木 ・構文木から不要な構文要素が除去  されたもの ・意味的な解析やプログラムの  最適化に適している SSA形式 ・SSA形式の構築後に得られる形式 ・各変数に一意な定義が存在し、  プログラムの再代入が存在しない ・ASTよりも高度な最適化が可能になる  ・変数のリネーム・共通部分式の除去   デッドコードの削除など ・データフローの解析が容易になる

Slide 31

Slide 31 text

31 3-3-1: SSA形式の構築 (unless)

Slide 32

Slide 32 text

32 3-3-1: SSA形式の構築 (unless) bEnd Cond

Slide 33

Slide 33 text

33 3-3-1: SSA形式の構築 (unless) bEnd bThen Cond

Slide 34

Slide 34 text

34 3-3-1: SSA形式の構築 (unless) bEnd bThen Cond

Slide 35

Slide 35 text

35 3-3-1: SSA形式の構築 bEnd bThen Cond

Slide 36

Slide 36 text

36 SSA形式のIRからマシン固有の値に置き換える処理 ・マシンに依存する最適化が実行される  ・値を利用箇所に近づける、レジスタ割り当てを行う、など ・最終的な出力はアセンブラへ渡され、実際の機械語が生成される  ・この機械語がリンクされて実行可能になる 3-3-2: 機械語生成

Slide 37

Slide 37 text

37 各パッケージの Dump関数 ・internal/syntaxならFdump(ref) ・internal/irならDump(ref) など The Go SSA Playground ・SSA等の情報をオンラインで生成してくれるツール ・ローカルで見たい時は、GOSSAFUNC=${FUNC_NAME} go build . ・詳しくはinternal/ssaのREADMEに 4. 解析に便利なツールほか

Slide 38

Slide 38 text

38 生成される SSAを見ると一致している (not(if-stmt), unless-stmt) 4. not(if-stmt) と unless-stmt の SSA の比較 not(if-stmt)から 生成されたSSA unless-stmtから 生成されたSSA

Slide 39

Slide 39 text

39 コンパイラの処理、ざっくり理解できましたか? 5. まとめ 1. 字句解析 2. 構文解析 3. 型チェック 4. IRの構築 1. ASTに対する最適化 2. ASTの巡回 1. SSA形式の構成 2. 機械語生成 フロントエンド ミドルエンド バックエンド 興味があれば、ご自身の環境で試してみてください! やり方はハンズオン形式で こちらのブログ にまとめてあります