SlideShare a Scribd company logo
1 of 59
FROM SOURCE TO BINARY
How Compiler Works

From source to binary
從原始碼到二進制                 Luse Cheng
ソースからバイナリへ               Sep 13, 2012 / 新竹碼農
Von Quelle Zu Binären
                              Luse Cheng
De source au binaire
                              Jim Huang
Desde fuente a binario        March 31, 2011 / 北科大
Binarium ut a fonte
自我介紹
 經歷
   Compiler Eng. at MediaTek Inc. (2011~Present)
   Compiler Lead at Andes Technology Corporation (2011~2011)
   Compiler Eng. at Andes Technology Corporation (2007~2011)


 國民革命軍軍人 (Andes Technology Corporation)
   從事 Compiler / Toolchain 研究, 開發
   從事 Android 開發
   從事 Open Source 工作 (GCC … etc)


 專門打雜的 Compiler 工程師 (MediaTek Inc.)
   應徵的時候是 LLVM 工程師
   事實上是專門打雜, Debug 的 Compiler 工程師
關於今天的演講
 摘要
  簡短的介紹從原始碼到二進制的流程
    三大步驟
    Compiler Driver
  從原始碼到二進制的重要推手: 編譯器
    課本中的編譯器
    真實世界的編譯器
        GCC
        LLVM
春秋: 微言大義
 隱公元年: 夏,五月,鄭伯克段於鄢


 短短幾個字,影含著諸多含意
  段不弟,故不言弟
  如二君,故曰克
  稱鄭伯,譏失教也


 為什麼要了解從原始碼到二進制
  電腦科學所有的問題都可以用另外一層的抽象化來解
   決 (All problems in computer science can be solved by
   another level of indirection)
  了解背後的運作原理, 破解微言大義
微言大義:
現代Linux環境, 程式啟動流程
                                                  動態連結
 Shell                                            靜態連結 hello

          ./hello                   ld-linux.so

                                             動態連結器
                                                   eglibc
                                 libc_start_main               main
          execve

                                      exit
         SYS_execve                 SYS_exit

kernel
         驗證執行檔        啟動loader    棄置Process

         配置Process
從原始碼到二進制:
常見的誤解
 從原始碼到二進制,其實不只需要 Compiler
 真相: 需要很多 Tool 同心協力一起完成 (Toolchain)
 真正的Compiler 其實只做下面的事…

     軟體                     硬體
   Source Code             HDL Source
                                              A compiler is a computer
                                              program (or set of programs)
                       Design Compiler (DC)   that transforms source code
Compiler (gcc, llvm)
                        RTL Compiler (RC)     written in a programming
                                              language (the source language)
     Selecting             Technology         into another computer
    Instruction             Mapping           language (the target language,
                                              often having a binary form
     Assembly                Netlist          known as object code)
                                                 source : http://en.wikipedia.org/wiki/Compiler
編譯 Hello world 程式的流程
       C program: hello.c 原始碼      三大步驟

        Compiler (CC1) 編譯器
                                     編譯
     Assembly File: hello.s 組合語言



        Assembler (AS) 組譯器           組譯

       Object File: hello.o 目的檔

                                     連結
          Linker (LD) 連結器



       Executable: hello 執行檔
圖解簡易完整編譯流程
                  #include <stdio.h>
                  int main() {
                    printf (“Hello World”);
                  }

  stdio.h    int printf (const char* fmt, …);
                                                         main:
Preprocess   int main() {                Compile             LDR R1, [R2 + 12]
               printf (“Hello World”);                       BAL printf
             }

                                                   libc.a 0x1000 <printf>:
                                                            EC 00 00 12
               Main:                                        …
  Assemble        EC 00 00 12            Linking
                  F0 ?? ?? ??                            0x2000 <Main>:
                                                            EC 00 00 12
                                                            F0 00 10 00
常見的誤解: GCC 是個 C 編譯器                                             gcc
 答案: GCC 是一個 Compiler Driver
                                                               binutils
              gcc               Source (.c and .h)              glibc

                      cpp
                                        Preprocessed source (.i or .ii)
                      cc1
                                        Asm (.s)
                      as
                                        Reloadable (.o)
                    collect2

 library

   crt*                               Binary
                           ld         (Executable)
link script
                     GCC 這個執行檔, 可以當Compiler, 也能當 Preprocessor,
                     也能當 Assembler, 更能當 Linker
Example: GCC 下給 ld 的參數
 collect2 version 4.4.3 (i386 Linux/ELF)

  /usr/bin/ld --build-id --eh-frame-hdr -m elf_i386 --hash-style=both
  -dynamic-linker /lib/ld-linux.so.2 -z relro /usr/lib/gcc/i486-linux-
  gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-
  gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/i486-linux-
  gnu/4.4.3/crtbegin.o -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -
  L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-
  gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-
  linux-gnu/4.4.3/../../.. hello.o -v -lgcc --as-needed -lgcc_s --no-as-
  needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-
  linux-gnu/4.4.3/crtend.o /usr/lib/gcc/i486-linux-
  gnu/4.4.3/../../../../lib/crtn.o
  GNU ld (GNU Binutils for Ubuntu) 2.20.1-system.20100303
如果有人想當你的程式碼
那你怎樣才能做她的編譯器 (Compiler) ?
 什麼是編譯器?
   Source Language ->
    Target Language

 Recognizer: 讀懂她
 Translator: 轉譯她
 Optimization: 調教她

        High Level Language


             Compiler


    Optimized Low Level Language
編譯器小歷史:
 First compiler (1952)
    第一個編譯器
    A-0, Grace Murray Hopper
 First complete compiler (1957)
    第一個完整的編譯器
    Fortran, John Backus (18-man years)                              Grace Hopper, inventor of
                                                                      A-0, COBOL, and the
 First multi-arch compiler (1960)                                    term “compiler.”
    第一個多平台編譯器
    Cobol, Grace Murray Hopper el al.
 The first self-hosting compiler (1962)
    第一個能自己編譯自己的編譯器
    LISP, Tim Hart and Mike Levin
Image source:
http://www-history.mcs.st-and.ac.uk/PictDisplay/Hopper.html
http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Grace_Hopper.jpg/300px-Grace_Hopper.jpg
先有雞還是先有蛋
 先有C語言還是先有C編譯器


 一個有趣的故事:
   Ken Thompson : Reflections on Trusting Trust
    (Compiling a compiler)


 Cross-compiling and Boot-strapping
   用X語言 (like Fortran or ASM) 寫一個C-的編譯器
   用C-寫一個C的編譯器
   用 C 寫一個C的編譯器
編譯器的架構 (Textbook)
                                      原始碼

                                字彙分析 (正規語言)

                           語法分析 (Context-Free Grammar)

                                      語法樹

                            語意分析 (Type Checking ..etc)

                                      中間語言

                                      最佳化

                                      目標語言


   屠龍刀: Syntax Directed Translator
編譯器技術=屠龍之技
 莊子: 列禦寇第三十二
  朱泙漫學屠龍於支離益,單千金之家,三年技成而無
  所用其巧。


 用白話說就是有個人花了很多錢和時間學了屠龍
技能, 後來發現沒有龍可以殺

 不過隨著硬體的發展,軟體的複雜度提升,這個世界
上的龍也越來越多了
while (y < z) {
    int x = a + b;
    y += x;
}                    How Compiler Works
   T_While

   T_LeftParen                         原始碼
   T_Identifier y

   T_Less                        字彙分析 (正規語言)
   T_Identifier z

   T_RightParen            語法分析 (Context-Free Grammar)
   T_OpenBrace

   T_Int                               語法樹
   T_Identifier x

   T_Assign
                            語意分析 (Type Checking ..etc)
   T_Identifier a

   T_Plus
                                      中間語言
   T_Identifier b

   T_Semicolon

   T_Identifier y
                                       最佳化
   T_PlusAssign

   T_Identifier x                     目標語言
   T_Semicolon

   T_CloseBrace
                           http://www.stanford.edu/class/cs143/
while (y < z) {
    int x = a + b;
    y += x;
}                    How Compiler Works
                                       原始碼

                                 字彙分析 (正規語言)

                           語法分析 (Context-Free Grammar)

                                       語法樹

                            語意分析 (Type Checking ..etc)

                                      中間語言

                                       最佳化

                                      目標語言


                           http://www.stanford.edu/class/cs143/
while (y < z) {
    int x = a + b;
    y += x;
}                    How Compiler Works
                                       原始碼

                                 字彙分析 (正規語言)

                           語法分析 (Context-Free Grammar)

                                       語法樹

                            語意分析 (Type Checking ..etc)

                                      中間語言

                                       最佳化

                                      目標語言


                           http://www.stanford.edu/class/cs143/
while (y < z) {
    int x = a + b;
    y += x;
}                           How Compiler Works
                                              原始碼
 Loop:   x=a+b
         y=x+y                          字彙分析 (正規語言)
         _t1 = y < z
         if _t1 goto Loop
                                  語法分析 (Context-Free Grammar)

         x=a+b                                語法樹
 Loop:   y=x+y
         _t1 = y < z               語意分析 (Type Checking ..etc)
         if _t1 goto Loop
                                             中間語言
         add $1, $2, $3
 loop:   add $4, $1, $4                       最佳化
         slt $6, $1, $5
         beq $6, loop
                                             目標語言


                                  http://www.stanford.edu/class/cs143/
真實世界中的編譯器
GCC, the GNU Compiler Collection
 海灣合作委員會 (X)


 GNU Compiler Collection (O)




         目前最多(*)使用者使用的Compiler(?)
真實世界中的編譯器
GCC, the GNU Compiler Collection
 從 GNU C Compiler 到 GNU Compiler Collection
   支援多種語言 (7), 多種處理器平台 (30+) (GCC 4.6.0)
   GCC 4.6.0 支援 GO 程式語言 (GCC-GO)
   GCC 4.6.0 超過 2 百萬行程式碼
   開放原始碼編譯器平台


 GCC = Compiler Driver
 CC1 = 真正的 C Compiler
GCC 的架構
 課本終究還是課本


 GCC 的特性與挑戰:                     int main()
   GCC 支援多種語言 (前端)               {
                                    return 0;
   GCC 支援多種處理器 (後端)                IamDeadCode();
   Syntax Directed Translator    }
      程式的語法樹結構將無法和處理器架構脫鉤


 仔細想想: 有些最佳化和語言跟平台都沒有關係
   消除沒有用到的程式碼 (Dead code elimination)


 所以 GCC 引入中間層最佳化 (編譯器相關最佳化)
GCC 的解決之道 (GCC 4.0 以後)
 C Source        C Front-End

C++ Source      C++ Front-End
                                       Generic   語言相關

                         Gimplify


     Gimple                                      編譯器相關
                         Tree SSA
                         Optimizer



                           RTL
                                        RTL      平台相關
   Target ASM            Optimizer
                      Code Generator               IR
資料流程分析 (Data-flow analysis)
 為什麼需要資料流程分析


 Q: 兩個指令怎樣才能交換位置 (Code Motion)
 A: 如果兩個指令沒有相依性的話


 資料流程分析 = 偵測指令相依性
 Reaching definition for a given instruction is another
  instruction, the target variable of which may reach the
  given instruction without an intervening assignment
   Compute use-def chains and def-use chains
Code Motion & Pointer Aliasing
  C 語言中阻擋 Compiler 最佳化的一大原因
     Pointer Aliasing: 兩個不同的指標指到同一個記憶體位置
      while (y < z) {                    int t1 = a + b;
        int x = a + b;                   while (y < z) {
        y += x;                            y += t1;
      }                                  }

 Q: How about this?
      void foo(int *a, int *b, int *y, int z) {
        while (*y < z) {
          int x = *a + *b;
          *y += x;
        }
      }

 A: Compiler 不能把*a + *b 移到迴圈外
   因為不確定使用者會不會這樣使用: foo(&num, &num, &num, 5566);
   這樣 a, b, y 互為別名, *y 的值改變會使 *a , *b 改變, 故不能提出到迴圈外
Strict Aliasing Rule & Restrict Pointer Aliasing
 如果 Pointer Aliasing 無限上綱
   那碰到 Pointer, Compiler 所有的最佳化都動不了
 To or not to do:
    Cross the Rubicon (O)
    蒯通說韓信自立為王 (X)
 Strict Aliasing Rule
    摩登編譯器會假設不同 type 之間的 pointer 是沒有 aliasing 的
    可是世界上存在相當多原始人程式碼, 所以就造成了很多問題
    Note: 使用 -fno-strict-aliasing 來關閉 Strict Aliasing Rule
 C99: Restrict Pointer Aliasing
   使用 restrict 關鍵字 (--std=c99) 來告訴 Compiler 這個 Pointer
    不會有 Pointer Aliasing
Static single assignment, SSA Form
 什麼是 SSA Form
   Social Security Administration (X)
                                         Diego Novillo, GCC Internals - IRs
   靜態單賦值形式 (O)
   静的単一代入 (O)

    用白話講, 在這個表示法當中
     每個被 Assign 的變數只會出現一次
    作法 1. 每個被Assign的變數給一個
     Version number 2. 使用 Φ functions

 Example: (From Wiki)
    INPUT: y = 1, y =2, x = y
    SSA: y1 = 1, y2 = 2, x1 = y2
使用 SSA 可以加強/加快以下的最佳化
 Constant propagation
 Sparse conditional constant propagation
 Dead code elimination
 Global value numbering
 Partial redundancy elimination
 Strength reduction
 Register allocation
SSA @ Constant propagation (常數傳遞)
   用了SSA後每次 Constant propagation 都考100分
        EX: GCC-3.x GCC-4.x Example
 GCC-3.x (No-SSA)                  int main()
                                   {
main:                                int a = 11,b = 13, c = 5566;
         …                           int i, result;
         mov        r4, #0           for (i = 0 ; i < 10000 ; i++)
.L5:                                   result = (a*b + i) / (a*c);
          mov       r1, #61184       return result;
          add       r0, r4, #143   }
          add       r1, r1, #42
          add       r4, r4, #1      GCC-4.x (SSA)
          bl        __divsi3
          cmp       r4, r5          main:
          ble       .L5                     mov     r0, #0
         …                                  bx      lr
GCC 前端: Source -> AST -> Generic
 GCC C/C++ frontend
   BISON (3.x)                                               b = (a - 56) / c
   Hand-written Recursive descent parser (4.x)
       C++ in 2004                                       =
       C and Objective-C in 2006


 辨識 C 原始碼, 並且轉換成剖析樹                              b                  /
  (Parsing)

                                                              -              c
 Abstract Syntax Tree (AST)
    抽象語法樹
    剖析樹 + 語意                                         a               56
    EX: b = (a - 56) / c
GCC中端: Gimple & Tree SSA Optimizer
 Gimple 是從 Generic Gimplify 而來的
   被限制每個運算只能有兩個運算元 (3-Address IR)
     t1 = A op B ( op is operator like +-*/ … etc )
   被限制語法只能有某些控制流程
   Gimple 可以被化簡成 SSA Form
   可以使用 -fdump-tree-<type>-<option> 來觀看其結構


 Tree SSA Optimizer
   100+ Passes
   Loop, Scalar optimization, alias analysis …etc
   Inter-procedural analysis, Inter-procedural optimization
GCC後端: Register Transfer Language (RTL)
                         b = a - 56

(set (reg:SI 60 [b])
     (plus:SI (reg:SI 61 [a])
              (const_int -56 [0xffffffc8])))

   LISP-Style 的表示法
   RTL Uses Virtual Register (無限多個 Register)
   Register Allocation (Virtual Register -> Hard Register)
   Instruction scheduling (Pipeline scheduling)
   GCC Built-in Operation and Arch-defined Operation
   Peephole optimization
GCC後端: RTL Patten Match Engine
                       (set (reg:SI 60 [b])
    MIPS.md                 (plus:SI (reg:SI 61 [a])
                                     (const_int -56 [0xffffffc8])))
(define_insn "*addsi3"
  [(set (match_operand:GPR 0 "register_operand" "=d,d")
        (plus:GPR (match_operand:GPR 1 "register_operand" "d,d")
                   (match_operand:GPR 2 "arith_operand" "d,Q")))]
  "!TARGET_MIPS16"
  "@
    addut%0,%1,%2                                   d代表是Register
    addiut%0,%1,%2"                                 Q代表是整數
  [(set_attr "type" "arith")
   (set_attr "mode" "si")])

          指令的屬性(用於管線排程)
                                                 b = a - 56
指令限制(非MIPS16才可使用)
                                               addiu $2, $3, -56
GCC後端 : 暫存器分配
 暫存器分配其實是個著色問題 (NP-Complete)
   給一個無向圖,相鄰的兩個vertex 不能著同一種顏色
   每個 vertex 代表的是一個變數, 每個 edge 代表的是兩個變數的生
    存時間有重疊
 GCC 的暫存器分配,其實比著色問題更複雜
   在有些平台,某些指令其實只能搭配某組的暫存器
   其實有些變數是常數,或是 memory form,或是他是參數或回傳值,所
    以只能分配到某些特定暫存器
 Old GCC RA: Reload Pass
   GCC 把 pseudo-registers 根據指令的限制對應到硬體暫存器的過程
   其實是個複雜到極點的程式, 所以到最後就沒人看得懂了
 所以後來就重寫了一個
   IRA (Integrated Register Allocator) : 整合 Coalescing, Live range
    splitting 以及挑選較好的 Hard Register 的做法
GCC後端 :管線排程
  以經典課本的五級Pipeline 為例子
   lw $8, a      IF   ID EX ME WB   IF   ID EX ME WB
   lw $9, b
                                                       Clock 0
 mul $10,$8,$8
                                                       Clock 1
add $11,$9,$10
                                                       Clock 2
   lw $12,c
                                                       Clock 3
add $13,$12,$0
                                                       Clock 4
   lw $8, a                                            Clock 5
   lw $9, b                                            Clock 6
   lw $12,c                                            Clock 7
 mul $10,$8,$8                                         Clock 8
add $13,$12,$0
GCC後端 :管線排程 (續)
 GCC use finite state automaton (FA) based pipeline
 scheduling model
GCC後端: Peephole optimization
 以管窺天最佳化法
 掃過整個IR, 看附近2 ~ n 個IR有沒有最佳化的機會
  感覺起來就像是用奧步
  可是如果奧步有用,那就得要好好使用
  在某些時候, 這個最佳化很好用, 尤其要生數據的時候 XD

    ;; sub rd, rn, #1
    ;; cmn rd, #1 (equivalent to cmp rd, #-1)
    ;; bne dest


    ;; subs rd, rn, #1
    ;; bcs dest ((unsigned)rn >= 1)

    ;; This is a common looping idiom (while (n--))
分很多個.c file or not 分很多個.c file
 為什麼要分很多檔案?
   因為要讓事情變得簡單
 為什麼我們在編譯程式的時候可以下 make -j24?
   因為編譯器在Compile 每個 .c 的時候視為獨立個體, 彼
    此之間沒有相依性
   用Compiler的術語稱之為 Compilation Unit
 Static Global Variable vs Non-Static Global Variable
   Static: 只有這個 Compilation Unit 可以看到這個變數
   Non-Static: 所有Compilation Unit 可以看到這個變數
      Q: 怎麼使用別的別的 Compilation Unit 內的變數
      A: 使用 extern 關鍵字. EX: extern int i;
深入淺出 Compilation Unit
Compilation Unit
                             Compilation Unit 產生的限制
  Internal Data
  Visible Data                關於沒有人使用的 Static Global
  External Data Reference
                               Variable
                                 Compiler 可以砍掉它來節省空間
  Internal Function
                              關於沒有人使用的 Non-Static
                               Global Variable
  Visible Function
                                 Compiler 不能砍掉它
                                 因為不能確定別的 Compilation
                                 Unit 會不會用到它
  External Func Reference
                             Note: 如果確定沒有別的檔案
  Internal Function
                             會使用到這個變數或函式, 請
                             宣告成 static
Compilation Unit
  盡可能使用區域變數
static int i;                             int main()
int main()                                {
{                                           int i;
  for (i = 0 ; i < 10; i++)                 for (i = 0 ; i < 10; i++)
    printf ("Hello %dn", i);                 printf ("Hello %dn", i);
}                                         }

main:                                     main:
        mov     r3, #0                            stmfd   sp!, {r4, lr}
                                                            11
        stmfd   sp!, {r4, lr}                     mov     r4, #0
        movw    r4, #:lower16:.LANCHOR0   .L2:
        movt    r4, #:upper16:.LANCHOR0           mov     r1, r4
        mov     r1, r3                            ldr     r0, .L4
        str     r3, [r4, #0]                      add     r4, r4, #1
.L2:                                              bl      printf
        ldr     r0, .L4                           cmp     r4, #10
        bl      printf                            bne     .L2
        ldr     r1, [r4, #0]                      ldmfd   sp!, {r4, pc}
        add     r1, r1, #1                .L4:
        str     r1, [r4, #0]                      .word   .LC0
        cmp     r1, #9
        ble     .L2
        ldmfd   sp!, {r4, pc}
.L4:
        .word   .LC0
GCC 內建函式 (Built-in Function)
 GCC 為了進行最佳化, 會辨認標準函式(因為語意有
 標準規範), 如果符合條件, 就會以處理器最適合的
 方式來計算
  Strcpy => x86 字串處理指令
  Printf => Puts, Putc
  Memcpy => Block Transfer Instruction


 這對開發嵌入式系統的人常造成困擾
  因為開發嵌入式系統的人喜歡開發自己殘廢的printf (X)
  因為開發嵌入式系統需要客製化的printf (O)
     Note: 使用 -fno-builtin-XXX or -fno-builtin 來關閉這個功能
IPO (Inter-Procedural Optimization)
 Procedure / Function: 結構化程式的主軸, 增加可用
 性, 減少維護成本
   副程式呼叫的執行成本
     準備參數: 把變數移到到特定 Register 或 push 到 Stack
     呼叫副程式, 儲存和回復 Register Context
   現代化程式: 有許多很小的副程式
     int IsOdd(int num) {
        return (num & 0x1);
      }
 最簡單的 IPO: Function inlining
   簡單來說: 把整個副程式copy 一份到 caller 的程式碼去
IPO (Inter-Procedural Optimization)
 如何 Inline 一個 外部函式?
    基本上 Compiler 無法做到
        因為 Compiler 在編譯程式的時候不會去看別的
           Compilation Unit 的內容, 不知道內容自然就沒辦法 inline
           一個看不見的函式
 解法: Link Time Optimization (LTO)
    或叫: Whole-Program Optimization (WPO)
    關鍵 Link Time Code Generation

Source 1      Compiler   IR 1    Linker   Full IR   Compiler

Source 2      Compiler   IR 2
                                                    Optimized
Source 3      Compiler   IR 3
                                                     Program
21世紀的另外一種選擇: LLVM
 LLVM (以前稱為 Low Level Virtual Machine)
   用 “現代“ C++ 寫成的 Compiler infrastructure
   設計來進行全時最佳化 (compile-time, link-time, run-
   time, and idle-time optimizations)

                         LLVM 起源
                            2000年UIUC Vikram Adve 與 Chris
                             Lattner 的研究發展而成
                         後來受到蘋果重用而大發異彩
                            蘋果概念股 (?)
                         FreeBSD 10 將使用 Clang/LLVM
                           為預設的編譯器
LLVM: 全時最佳化編譯器平台
   GNU Toolchain 的守備範圍    知道原始碼的結構 (O)
   編譯階段
                          不知道變數的位址 (X)

                          不知道原始碼的結構 (X)
   連結階段
                          知道變數的位址 (O)

                          不知道原始碼的結構 (X)
  載入/安裝階段
                          不知道變數的位址 (X)
                          知道處理器有甚麼能力 (O)
   執行階段
                         正所謂是
                         橫看成嶺側成峰
   空閒階段
                         遠近高低各不同
LLVM核心觀念:
LLVM is a Compiler IR
 既然 Compiler framework 就是要支援多種 Front-end 和
 多種 Back-end
   使用 RISC-Like 平台無關的指令 (LLVM Instruction)和資料
    (Metadata) 來表達原始碼
 LLVM IR 的三變化 (等價形式)
   In-Memory Form: JIT
   Assembly language representation: ASM format (.ll)
   Bitcode representation: Object code format (.bc)
 特點
   Self-contained Representation 且有良好規範的 Compiler IR
   可以將 Compiler 到一半的結果序列化
LLVM 的架構 : 從原始碼到二進制
                                  LLVM Optimizer
 C Front-End                          BitCode             Stream-out
                LLVM bitcode          Passes               Optimized
C++ Front-End                                            LLVM bitcode
                                   Selection DAG
                                       Passes
… Front-End                          Machine
                                      Passes


Not in LLVM         LLVM C/C++         LLVM ARM          LLVM x86
                      Back-End          Back-End         Back-End

  In LLVM          C/C++ Source        ARM ASM           X86 ASM
 Use LLVM
  Target                              ARM Object        X86 Object

                                     Execution (JIT)   Execution (JIT)
LLVM Toolchain (llvm-gcc)
 既然 LLVM 只是一個 Compiler-IR, 就代表 LLVM 在剛
  開始的時候是沒有辦法走完整個編譯流程的
 LLVM-GCC (gcc-4.2)
     利用 GCC 已經存在而且眾多大眾使用的 Front-end
     後來 RMS 就出來打槍這種方式 (GCC 4.3, GPLv3)
     後來 Apple 就跳出來發展自己的 Front-end (Clang)

 C Source            C Front-End
                                              Generic
C++ Source       C++ Front-End

                                   Gimplify

            Gimple                             LLVM Bitcode

NOTE: LLVM早期的也沒有自己的 Assembler (後來有 MC) 和 Linker 來建立完整編譯流程
LLVM Toolchain (Clang)
 Clang : C, C++, Objective-C 的編譯器前端
   Apple 為了取代 GCC 重新打造的 Frontend
   重寫一個前端代價是非常昂貴的
     其實你是 慣C? 還是 慣GCC?
     GNU/Linux 世界使用了 GCC 行之有年的非標準語法
   Clang 設計成模組化 Frontend : Clang C API
     可以 Export AST (Ex: RenderScript 用來產生 Java 的 Binding)
     可以用來實作自動完成, Refactor Tool … etc
     可以用來實作靜態程式碼分析工具 (Linting tool … etc)
   Clang 改善了錯誤訊息 (Expressive diagnostics )
   快速編譯, 低記憶使用量, 容易學習和Hack的 Frontend
範例: Clang Expressive Diagnostics
               struct a {
                 virtual int bar();
               };
               struct foo : public virtual a {
               };
               void test(foo *P) {
                 return P->bar() + *P;
               }

g++-4.7
xx.cc: In function 'void test(foo*)':
xx.cc:9:24: error: no match for 'operator+' in '(((a*)P) + ((sizetype)(*(long int*)(P-
>foo::<anonymous>.a::_vptr.a + -32u))))->a::bar() + * P'
xx.cc:9:24: error: return-statement with a value, in function returning 'void' [-fpermissive]

clang-3.1
xx.cc:9:21: error: invalid operands to binary expression ('int' and 'foo')
   return P->bar() + *P;
       ~~~~~~~~ ^ ~~
Clang/LLVM Toolchain
 C Source                   CLANG
                             Language                          LLVM Bitcode
C++ Source                   Optimizer


 Binding Script .. etc       AST Stream-out


 Clang Compiler Driver
    盡可能和 GCC 相容, 遇到不認識的Option不會停下來
    不需要 Cross Compiler, 本身就有 Cross Compiler 的能力 (?)
          Cross Compiler != Cross Compiler Toolchian




                             來源: http://www.aosabook.org/en/llvm.html
滅諸侯,成帝業,為天下一統


LLVM 千秋萬世, 一統江湖
 Q:挖賽, Clang + LLVM 那麼神, 其他人不是沒戲唱了嗎
   A: 在這地球上還沒有人能統一天下, LLVM 當然也不行
 Clang 之所以不用產生 Cross Compiler 的迷思
    x86/64 clang 產生出來的LLVM Bitcode, 用 i686 的 llvm
     backend 來產生 x86 的機器碼 (-m32)
      可以產生出來, 可是不會動 XD
 C/C++ 不是 Portable Language, 產生出來的 Bitcode 不
 Portable 不是也是很正常的事嗎
   LLVM Bitcode 本身就是 Portable (X)
   使用 host triple 來改變產生出來的 LLVM Bitcode (O)
LLVM TableGen (Target Description)
         Instruction Encoding (For MC Assembler)
                         Input/Output
                                           Assembly (For ASMPrinter)
def ADDPS : PI<0x58,                          Instruction Selection Pattern
    (outs VR256:$dst),                        (For Selection DAG)
    (ins VR256:$src1, VR256:$src2),
    "addps $src2, $src1, $dst",
    [(set VR256:$dst, (fadd VR256:$src1, VR256:$src2))]>;


 編譯器Backend 都有一些很無聊的地方, 用另外一
 種語言來表示比直接寫 C/C++ 簡單好 maintain 的
   GCC RTL Pattern : (MD, Machine Description file)
   LLVM TableGen : (TD, Target Description file)
完美Compiler進化論: LLVM的多變化
Source 1        Frontend      LLVM IR 1             LLVM Link
                                                                              IPOed
                                                                           executable/
Source 2        Frontend      LLVM IR 2          LLVM Optimizer
                                                                           Share object
                                                    LLVM ARM
Source 3        Frontend      LLVM IR 3
                                                     Backend
               Compile Time                                          Link Time

Source 1        Frontend         LLVM IR 1           LLVM Link               LLVM ARM Backend

Source 2        Frontend         LLVM IR 2        LLVM Optimizer                 Processor Specific
                                                                                    executable/
Source 3        Frontend         LLVM IR 3                                         Share object
               Compile Time                              Link Time               Load /Installation Time

 LLVM IR
                                                                        PGOed/ Runtime Opted
                LLVM Optimizer          LLVM ARM Backend                executable/ Share object
Profile Data
           Idle Time                         Load Time
LLVM 的發展
 史記: 項羽本紀第七
   太史公曰:吾聞之周生曰「舜目蓋重瞳子」,又聞項羽亦重瞳子。
    羽豈其苗裔邪?何興之暴也!
   LLVM 也有兩個 L, 加上一家可以掌握全局垂直整合的公司在後面力
    挺, LLVM 也是銳不可擋
 最美好的時代, 最壞得的時代
   LLVM Bitcode 用來當傳遞格式還有很多問題
       Binary Compatibility 最為人所詬病
       http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-
        20120521/143197.html
 CASE STUDY: OpenCL SPIR
    The OpenCL Khronos group proposes to extend LLVM to be the
     standard OpenCL IR (called SPIR)
       http://www.khronos.org/registry/cl/specs/spir_spec-1.0-provisional.pdf
    Khronos SPIR For OpenCL Brings Binary Compatibility
       http://www.phoronix.com/scan.php?page=news_item&px=MTE4MzM
相信你的編譯器
 Linux-Kongress 2009: Source Code Optimization
    http://www.linux-kongress.org/2009/slides/compiler_survey_
     felix_von_leitner.pdf

 Q: x86 中把一個暫存器清成0
   mov $0,%eax
   and $0,%eax
   sub %eax,%eax
   xor %eax,%eax


 哪道指令最好?
相信你的編譯器: 解答
 x86 的指令是可變長度的:
   b8 00 00 00 00       mov $0,%eax
   83 e0 00             and $0,%eax
   29 c0                sub %eax,%eax
   31 c0                xor %eax,%eax


 So, sub or xor? Turns out, both produce a false
 dependency on %eax. But CPUs know to ignore it for
 xor.
References
 http://www.redhat.com/magazine/002dec04/features/gcc/
 http://gcc.gnu.org/onlinedocs/gcc
 http://en.wikipedia.org/wiki/Interprocedural_optimization
 http://en.wikipedia.org/wiki/Link-time_optimization
 http://llvm.org/
 http://www.aosabook.org/en/llvm.html
Any Question ?

More Related Content

What's hot

COSCUP 2016 - LLVM 由淺入淺
COSCUP 2016 - LLVM 由淺入淺COSCUP 2016 - LLVM 由淺入淺
COSCUP 2016 - LLVM 由淺入淺宗凡 楊
 
Android C Library: Bionic 成長計畫
Android C Library: Bionic 成長計畫Android C Library: Bionic 成長計畫
Android C Library: Bionic 成長計畫Kito Cheng
 
Hello world在那邊?背景說明
Hello world在那邊?背景說明Hello world在那邊?背景說明
Hello world在那邊?背景說明Wen Liao
 
FreeBSD documentation translation
FreeBSD documentation translationFreeBSD documentation translation
FreeBSD documentation translationRaycherng Yu
 
NCURSES Programming HOWTO
NCURSES Programming HOWTONCURSES Programming HOWTO
NCURSES Programming HOWTOeastsky
 
Light talk @ coscup 2011 : Incremental Global Prelink for Android
Light talk @ coscup 2011 : Incremental Global Prelink for AndroidLight talk @ coscup 2011 : Incremental Global Prelink for Android
Light talk @ coscup 2011 : Incremental Global Prelink for AndroidKito Cheng
 
少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月鍾誠 陳鍾誠
 
嵌入式平台移植技巧概說
嵌入式平台移植技巧概說嵌入式平台移植技巧概說
嵌入式平台移植技巧概說Joseph Lu
 
系統程式 -- 第 1 章 系統軟體
系統程式 -- 第 1 章 系統軟體系統程式 -- 第 1 章 系統軟體
系統程式 -- 第 1 章 系統軟體鍾誠 陳鍾誠
 
C++工程实践
C++工程实践C++工程实践
C++工程实践Shuo Chen
 
Recycle Open Source Projects
Recycle Open Source ProjectsRecycle Open Source Projects
Recycle Open Source ProjectsGeorge Ang
 
系統程式 -- 第 12 章
系統程式 -- 第 12 章系統程式 -- 第 12 章
系統程式 -- 第 12 章鍾誠 陳鍾誠
 
Python topic re
Python topic rePython topic re
Python topic recri fan
 
Learning python in the motion picture industry by will zhou
Learning python in the motion picture industry   by will zhouLearning python in the motion picture industry   by will zhou
Learning python in the motion picture industry by will zhouWill Zhou
 

What's hot (19)

COSCUP 2016 - LLVM 由淺入淺
COSCUP 2016 - LLVM 由淺入淺COSCUP 2016 - LLVM 由淺入淺
COSCUP 2016 - LLVM 由淺入淺
 
Android C Library: Bionic 成長計畫
Android C Library: Bionic 成長計畫Android C Library: Bionic 成長計畫
Android C Library: Bionic 成長計畫
 
Hello world在那邊?背景說明
Hello world在那邊?背景說明Hello world在那邊?背景說明
Hello world在那邊?背景說明
 
系統程式 -- 第 5 章
系統程式 -- 第 5 章系統程式 -- 第 5 章
系統程式 -- 第 5 章
 
FreeBSD documentation translation
FreeBSD documentation translationFreeBSD documentation translation
FreeBSD documentation translation
 
NCURSES Programming HOWTO
NCURSES Programming HOWTONCURSES Programming HOWTO
NCURSES Programming HOWTO
 
系統程式 -- 第 4 章
系統程式 -- 第 4 章系統程式 -- 第 4 章
系統程式 -- 第 4 章
 
Light talk @ coscup 2011 : Incremental Global Prelink for Android
Light talk @ coscup 2011 : Incremental Global Prelink for AndroidLight talk @ coscup 2011 : Incremental Global Prelink for Android
Light talk @ coscup 2011 : Incremental Global Prelink for Android
 
少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月
 
系統程式 - 附錄
系統程式 - 附錄系統程式 - 附錄
系統程式 - 附錄
 
回顧 GNU/Linux 中文資訊化進展與未來機會
回顧 GNU/Linux 中文資訊化進展與未來機會回顧 GNU/Linux 中文資訊化進展與未來機會
回顧 GNU/Linux 中文資訊化進展與未來機會
 
嵌入式平台移植技巧概說
嵌入式平台移植技巧概說嵌入式平台移植技巧概說
嵌入式平台移植技巧概說
 
系統程式 -- 第 1 章 系統軟體
系統程式 -- 第 1 章 系統軟體系統程式 -- 第 1 章 系統軟體
系統程式 -- 第 1 章 系統軟體
 
C++工程实践
C++工程实践C++工程实践
C++工程实践
 
軟體工程(總結篇)
軟體工程(總結篇)軟體工程(總結篇)
軟體工程(總結篇)
 
Recycle Open Source Projects
Recycle Open Source ProjectsRecycle Open Source Projects
Recycle Open Source Projects
 
系統程式 -- 第 12 章
系統程式 -- 第 12 章系統程式 -- 第 12 章
系統程式 -- 第 12 章
 
Python topic re
Python topic rePython topic re
Python topic re
 
Learning python in the motion picture industry by will zhou
Learning python in the motion picture industry   by will zhouLearning python in the motion picture industry   by will zhou
Learning python in the motion picture industry by will zhou
 

Similar to Hcsm lect-20120913

COSCUP2016 - LLVM框架、由淺入淺
COSCUP2016 - LLVM框架、由淺入淺COSCUP2016 - LLVM框架、由淺入淺
COSCUP2016 - LLVM框架、由淺入淺hydai
 
函数调用关系工具-2011-孙光福
函数调用关系工具-2011-孙光福函数调用关系工具-2011-孙光福
函数调用关系工具-2011-孙光福Wu Liang
 
ajax_onlinemad
ajax_onlinemadajax_onlinemad
ajax_onlinemadKitor23
 
Introduce to Linux command line
Introduce to Linux command lineIntroduce to Linux command line
Introduce to Linux command lineWen Liao
 
Introduction to C++ over CLI
Introduction to C++ over CLIIntroduction to C++ over CLI
Introduction to C++ over CLI建興 王
 
Introduction To Direct Show
Introduction To Direct ShowIntroduction To Direct Show
Introduction To Direct Showguestce3937
 
Introduction To Direct Show
Introduction To  Direct ShowIntroduction To  Direct Show
Introduction To Direct Showguestce3937
 
漫談 Source Control Management
漫談 Source Control Management漫談 Source Control Management
漫談 Source Control ManagementWen-Shih Chao
 
函数调用关系工具-Lite
函数调用关系工具-Lite函数调用关系工具-Lite
函数调用关系工具-LiteWu Liang
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509tidesq
 

Similar to Hcsm lect-20120913 (20)

Ch01
Ch01Ch01
Ch01
 
C+
C+C+
C+
 
C#
C#C#
C#
 
COSCUP2016 - LLVM框架、由淺入淺
COSCUP2016 - LLVM框架、由淺入淺COSCUP2016 - LLVM框架、由淺入淺
COSCUP2016 - LLVM框架、由淺入淺
 
系統程式 -- 第 8 章
系統程式 -- 第 8 章系統程式 -- 第 8 章
系統程式 -- 第 8 章
 
函数调用关系工具-2011-孙光福
函数调用关系工具-2011-孙光福函数调用关系工具-2011-孙光福
函数调用关系工具-2011-孙光福
 
C 1 c
C 1 cC 1 c
C 1 c
 
C 1 c
C 1 cC 1 c
C 1 c
 
C 1 c
C 1 cC 1 c
C 1 c
 
C 1 c
C 1 cC 1 c
C 1 c
 
ajax_onlinemad
ajax_onlinemadajax_onlinemad
ajax_onlinemad
 
Introduce to Linux command line
Introduce to Linux command lineIntroduce to Linux command line
Introduce to Linux command line
 
Introduction to C++ over CLI
Introduction to C++ over CLIIntroduction to C++ over CLI
Introduction to C++ over CLI
 
Introduction To Direct Show
Introduction To Direct ShowIntroduction To Direct Show
Introduction To Direct Show
 
Introduction To Direct Show
Introduction To  Direct ShowIntroduction To  Direct Show
Introduction To Direct Show
 
Build Your Own Android Toolchain from scratch
Build Your Own Android Toolchain from scratchBuild Your Own Android Toolchain from scratch
Build Your Own Android Toolchain from scratch
 
漫談 Source Control Management
漫談 Source Control Management漫談 Source Control Management
漫談 Source Control Management
 
函数调用关系工具-Lite
函数调用关系工具-Lite函数调用关系工具-Lite
函数调用关系工具-Lite
 
第1章 概论
第1章 概论第1章 概论
第1章 概论
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
 

Hcsm lect-20120913

  • 1. FROM SOURCE TO BINARY How Compiler Works From source to binary 從原始碼到二進制 Luse Cheng ソースからバイナリへ Sep 13, 2012 / 新竹碼農 Von Quelle Zu Binären Luse Cheng De source au binaire Jim Huang Desde fuente a binario March 31, 2011 / 北科大 Binarium ut a fonte
  • 2. 自我介紹  經歷  Compiler Eng. at MediaTek Inc. (2011~Present)  Compiler Lead at Andes Technology Corporation (2011~2011)  Compiler Eng. at Andes Technology Corporation (2007~2011)  國民革命軍軍人 (Andes Technology Corporation)  從事 Compiler / Toolchain 研究, 開發  從事 Android 開發  從事 Open Source 工作 (GCC … etc)  專門打雜的 Compiler 工程師 (MediaTek Inc.)  應徵的時候是 LLVM 工程師  事實上是專門打雜, Debug 的 Compiler 工程師
  • 3. 關於今天的演講  摘要  簡短的介紹從原始碼到二進制的流程  三大步驟  Compiler Driver  從原始碼到二進制的重要推手: 編譯器  課本中的編譯器  真實世界的編譯器  GCC  LLVM
  • 4. 春秋: 微言大義  隱公元年: 夏,五月,鄭伯克段於鄢  短短幾個字,影含著諸多含意  段不弟,故不言弟  如二君,故曰克  稱鄭伯,譏失教也  為什麼要了解從原始碼到二進制  電腦科學所有的問題都可以用另外一層的抽象化來解 決 (All problems in computer science can be solved by another level of indirection)  了解背後的運作原理, 破解微言大義
  • 5. 微言大義: 現代Linux環境, 程式啟動流程 動態連結 Shell 靜態連結 hello ./hello ld-linux.so 動態連結器 eglibc libc_start_main main execve exit SYS_execve SYS_exit kernel 驗證執行檔 啟動loader 棄置Process 配置Process
  • 6. 從原始碼到二進制: 常見的誤解  從原始碼到二進制,其實不只需要 Compiler  真相: 需要很多 Tool 同心協力一起完成 (Toolchain)  真正的Compiler 其實只做下面的事… 軟體 硬體 Source Code HDL Source A compiler is a computer program (or set of programs) Design Compiler (DC) that transforms source code Compiler (gcc, llvm) RTL Compiler (RC) written in a programming language (the source language) Selecting Technology into another computer Instruction Mapping language (the target language, often having a binary form Assembly Netlist known as object code) source : http://en.wikipedia.org/wiki/Compiler
  • 7. 編譯 Hello world 程式的流程 C program: hello.c 原始碼 三大步驟 Compiler (CC1) 編譯器 編譯 Assembly File: hello.s 組合語言 Assembler (AS) 組譯器 組譯 Object File: hello.o 目的檔 連結 Linker (LD) 連結器 Executable: hello 執行檔
  • 8. 圖解簡易完整編譯流程 #include <stdio.h> int main() { printf (“Hello World”); } stdio.h int printf (const char* fmt, …); main: Preprocess int main() { Compile LDR R1, [R2 + 12] printf (“Hello World”); BAL printf } libc.a 0x1000 <printf>: EC 00 00 12 Main: … Assemble EC 00 00 12 Linking F0 ?? ?? ?? 0x2000 <Main>: EC 00 00 12 F0 00 10 00
  • 9. 常見的誤解: GCC 是個 C 編譯器 gcc 答案: GCC 是一個 Compiler Driver binutils gcc Source (.c and .h) glibc cpp Preprocessed source (.i or .ii) cc1 Asm (.s) as Reloadable (.o) collect2 library crt* Binary ld (Executable) link script GCC 這個執行檔, 可以當Compiler, 也能當 Preprocessor, 也能當 Assembler, 更能當 Linker
  • 10. Example: GCC 下給 ld 的參數  collect2 version 4.4.3 (i386 Linux/ELF) /usr/bin/ld --build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -z relro /usr/lib/gcc/i486-linux- gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/i486-linux- gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/i486-linux- gnu/4.4.3/crtbegin.o -L/usr/lib/gcc/i486-linux-gnu/4.4.3 - L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux- gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486- linux-gnu/4.4.3/../../.. hello.o -v -lgcc --as-needed -lgcc_s --no-as- needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486- linux-gnu/4.4.3/crtend.o /usr/lib/gcc/i486-linux- gnu/4.4.3/../../../../lib/crtn.o GNU ld (GNU Binutils for Ubuntu) 2.20.1-system.20100303
  • 11. 如果有人想當你的程式碼 那你怎樣才能做她的編譯器 (Compiler) ?  什麼是編譯器?  Source Language -> Target Language  Recognizer: 讀懂她  Translator: 轉譯她  Optimization: 調教她 High Level Language Compiler Optimized Low Level Language
  • 12. 編譯器小歷史:  First compiler (1952)  第一個編譯器  A-0, Grace Murray Hopper  First complete compiler (1957)  第一個完整的編譯器  Fortran, John Backus (18-man years) Grace Hopper, inventor of A-0, COBOL, and the  First multi-arch compiler (1960) term “compiler.”  第一個多平台編譯器  Cobol, Grace Murray Hopper el al.  The first self-hosting compiler (1962)  第一個能自己編譯自己的編譯器  LISP, Tim Hart and Mike Levin Image source: http://www-history.mcs.st-and.ac.uk/PictDisplay/Hopper.html http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Grace_Hopper.jpg/300px-Grace_Hopper.jpg
  • 13. 先有雞還是先有蛋  先有C語言還是先有C編譯器  一個有趣的故事:  Ken Thompson : Reflections on Trusting Trust (Compiling a compiler)  Cross-compiling and Boot-strapping  用X語言 (like Fortran or ASM) 寫一個C-的編譯器  用C-寫一個C的編譯器  用 C 寫一個C的編譯器
  • 14. 編譯器的架構 (Textbook) 原始碼 字彙分析 (正規語言) 語法分析 (Context-Free Grammar) 語法樹 語意分析 (Type Checking ..etc) 中間語言 最佳化 目標語言  屠龍刀: Syntax Directed Translator
  • 15. 編譯器技術=屠龍之技  莊子: 列禦寇第三十二  朱泙漫學屠龍於支離益,單千金之家,三年技成而無 所用其巧。  用白話說就是有個人花了很多錢和時間學了屠龍 技能, 後來發現沒有龍可以殺  不過隨著硬體的發展,軟體的複雜度提升,這個世界 上的龍也越來越多了
  • 16. while (y < z) { int x = a + b; y += x; } How Compiler Works T_While T_LeftParen 原始碼 T_Identifier y T_Less 字彙分析 (正規語言) T_Identifier z T_RightParen 語法分析 (Context-Free Grammar) T_OpenBrace T_Int 語法樹 T_Identifier x T_Assign 語意分析 (Type Checking ..etc) T_Identifier a T_Plus 中間語言 T_Identifier b T_Semicolon T_Identifier y 最佳化 T_PlusAssign T_Identifier x 目標語言 T_Semicolon T_CloseBrace http://www.stanford.edu/class/cs143/
  • 17. while (y < z) { int x = a + b; y += x; } How Compiler Works 原始碼 字彙分析 (正規語言) 語法分析 (Context-Free Grammar) 語法樹 語意分析 (Type Checking ..etc) 中間語言 最佳化 目標語言 http://www.stanford.edu/class/cs143/
  • 18. while (y < z) { int x = a + b; y += x; } How Compiler Works 原始碼 字彙分析 (正規語言) 語法分析 (Context-Free Grammar) 語法樹 語意分析 (Type Checking ..etc) 中間語言 最佳化 目標語言 http://www.stanford.edu/class/cs143/
  • 19. while (y < z) { int x = a + b; y += x; } How Compiler Works 原始碼 Loop: x=a+b y=x+y 字彙分析 (正規語言) _t1 = y < z if _t1 goto Loop 語法分析 (Context-Free Grammar) x=a+b 語法樹 Loop: y=x+y _t1 = y < z 語意分析 (Type Checking ..etc) if _t1 goto Loop 中間語言 add $1, $2, $3 loop: add $4, $1, $4 最佳化 slt $6, $1, $5 beq $6, loop 目標語言 http://www.stanford.edu/class/cs143/
  • 20. 真實世界中的編譯器 GCC, the GNU Compiler Collection  海灣合作委員會 (X)  GNU Compiler Collection (O) 目前最多(*)使用者使用的Compiler(?)
  • 21. 真實世界中的編譯器 GCC, the GNU Compiler Collection  從 GNU C Compiler 到 GNU Compiler Collection  支援多種語言 (7), 多種處理器平台 (30+) (GCC 4.6.0)  GCC 4.6.0 支援 GO 程式語言 (GCC-GO)  GCC 4.6.0 超過 2 百萬行程式碼  開放原始碼編譯器平台  GCC = Compiler Driver  CC1 = 真正的 C Compiler
  • 22. GCC 的架構  課本終究還是課本  GCC 的特性與挑戰: int main()  GCC 支援多種語言 (前端) { return 0;  GCC 支援多種處理器 (後端) IamDeadCode();  Syntax Directed Translator }  程式的語法樹結構將無法和處理器架構脫鉤  仔細想想: 有些最佳化和語言跟平台都沒有關係  消除沒有用到的程式碼 (Dead code elimination)  所以 GCC 引入中間層最佳化 (編譯器相關最佳化)
  • 23. GCC 的解決之道 (GCC 4.0 以後) C Source C Front-End C++ Source C++ Front-End Generic 語言相關 Gimplify Gimple 編譯器相關 Tree SSA Optimizer RTL RTL 平台相關 Target ASM Optimizer Code Generator IR
  • 24. 資料流程分析 (Data-flow analysis)  為什麼需要資料流程分析  Q: 兩個指令怎樣才能交換位置 (Code Motion)  A: 如果兩個指令沒有相依性的話  資料流程分析 = 偵測指令相依性  Reaching definition for a given instruction is another instruction, the target variable of which may reach the given instruction without an intervening assignment  Compute use-def chains and def-use chains
  • 25. Code Motion & Pointer Aliasing  C 語言中阻擋 Compiler 最佳化的一大原因  Pointer Aliasing: 兩個不同的指標指到同一個記憶體位置 while (y < z) { int t1 = a + b; int x = a + b; while (y < z) { y += x; y += t1; } } Q: How about this? void foo(int *a, int *b, int *y, int z) { while (*y < z) { int x = *a + *b; *y += x; } }  A: Compiler 不能把*a + *b 移到迴圈外  因為不確定使用者會不會這樣使用: foo(&num, &num, &num, 5566);  這樣 a, b, y 互為別名, *y 的值改變會使 *a , *b 改變, 故不能提出到迴圈外
  • 26. Strict Aliasing Rule & Restrict Pointer Aliasing  如果 Pointer Aliasing 無限上綱  那碰到 Pointer, Compiler 所有的最佳化都動不了  To or not to do:  Cross the Rubicon (O)  蒯通說韓信自立為王 (X)  Strict Aliasing Rule  摩登編譯器會假設不同 type 之間的 pointer 是沒有 aliasing 的  可是世界上存在相當多原始人程式碼, 所以就造成了很多問題  Note: 使用 -fno-strict-aliasing 來關閉 Strict Aliasing Rule  C99: Restrict Pointer Aliasing  使用 restrict 關鍵字 (--std=c99) 來告訴 Compiler 這個 Pointer 不會有 Pointer Aliasing
  • 27. Static single assignment, SSA Form  什麼是 SSA Form  Social Security Administration (X) Diego Novillo, GCC Internals - IRs  靜態單賦值形式 (O)  静的単一代入 (O)  用白話講, 在這個表示法當中 每個被 Assign 的變數只會出現一次  作法 1. 每個被Assign的變數給一個 Version number 2. 使用 Φ functions  Example: (From Wiki)  INPUT: y = 1, y =2, x = y  SSA: y1 = 1, y2 = 2, x1 = y2
  • 28. 使用 SSA 可以加強/加快以下的最佳化  Constant propagation  Sparse conditional constant propagation  Dead code elimination  Global value numbering  Partial redundancy elimination  Strength reduction  Register allocation
  • 29. SSA @ Constant propagation (常數傳遞)  用了SSA後每次 Constant propagation 都考100分  EX: GCC-3.x GCC-4.x Example GCC-3.x (No-SSA) int main() { main: int a = 11,b = 13, c = 5566; … int i, result; mov r4, #0 for (i = 0 ; i < 10000 ; i++) .L5: result = (a*b + i) / (a*c); mov r1, #61184 return result; add r0, r4, #143 } add r1, r1, #42 add r4, r4, #1 GCC-4.x (SSA) bl __divsi3 cmp r4, r5 main: ble .L5 mov r0, #0 … bx lr
  • 30. GCC 前端: Source -> AST -> Generic  GCC C/C++ frontend  BISON (3.x) b = (a - 56) / c  Hand-written Recursive descent parser (4.x)  C++ in 2004 =  C and Objective-C in 2006  辨識 C 原始碼, 並且轉換成剖析樹 b / (Parsing) - c  Abstract Syntax Tree (AST)  抽象語法樹  剖析樹 + 語意 a 56  EX: b = (a - 56) / c
  • 31. GCC中端: Gimple & Tree SSA Optimizer  Gimple 是從 Generic Gimplify 而來的  被限制每個運算只能有兩個運算元 (3-Address IR)  t1 = A op B ( op is operator like +-*/ … etc )  被限制語法只能有某些控制流程  Gimple 可以被化簡成 SSA Form  可以使用 -fdump-tree-<type>-<option> 來觀看其結構  Tree SSA Optimizer  100+ Passes  Loop, Scalar optimization, alias analysis …etc  Inter-procedural analysis, Inter-procedural optimization
  • 32. GCC後端: Register Transfer Language (RTL) b = a - 56 (set (reg:SI 60 [b]) (plus:SI (reg:SI 61 [a]) (const_int -56 [0xffffffc8])))  LISP-Style 的表示法  RTL Uses Virtual Register (無限多個 Register)  Register Allocation (Virtual Register -> Hard Register)  Instruction scheduling (Pipeline scheduling)  GCC Built-in Operation and Arch-defined Operation  Peephole optimization
  • 33. GCC後端: RTL Patten Match Engine (set (reg:SI 60 [b]) MIPS.md (plus:SI (reg:SI 61 [a]) (const_int -56 [0xffffffc8]))) (define_insn "*addsi3" [(set (match_operand:GPR 0 "register_operand" "=d,d") (plus:GPR (match_operand:GPR 1 "register_operand" "d,d") (match_operand:GPR 2 "arith_operand" "d,Q")))] "!TARGET_MIPS16" "@ addut%0,%1,%2 d代表是Register addiut%0,%1,%2" Q代表是整數 [(set_attr "type" "arith") (set_attr "mode" "si")]) 指令的屬性(用於管線排程) b = a - 56 指令限制(非MIPS16才可使用) addiu $2, $3, -56
  • 34. GCC後端 : 暫存器分配  暫存器分配其實是個著色問題 (NP-Complete)  給一個無向圖,相鄰的兩個vertex 不能著同一種顏色  每個 vertex 代表的是一個變數, 每個 edge 代表的是兩個變數的生 存時間有重疊  GCC 的暫存器分配,其實比著色問題更複雜  在有些平台,某些指令其實只能搭配某組的暫存器  其實有些變數是常數,或是 memory form,或是他是參數或回傳值,所 以只能分配到某些特定暫存器  Old GCC RA: Reload Pass  GCC 把 pseudo-registers 根據指令的限制對應到硬體暫存器的過程  其實是個複雜到極點的程式, 所以到最後就沒人看得懂了  所以後來就重寫了一個  IRA (Integrated Register Allocator) : 整合 Coalescing, Live range splitting 以及挑選較好的 Hard Register 的做法
  • 35. GCC後端 :管線排程  以經典課本的五級Pipeline 為例子 lw $8, a IF ID EX ME WB IF ID EX ME WB lw $9, b Clock 0 mul $10,$8,$8 Clock 1 add $11,$9,$10 Clock 2 lw $12,c Clock 3 add $13,$12,$0 Clock 4 lw $8, a Clock 5 lw $9, b Clock 6 lw $12,c Clock 7 mul $10,$8,$8 Clock 8 add $13,$12,$0
  • 36. GCC後端 :管線排程 (續)  GCC use finite state automaton (FA) based pipeline scheduling model
  • 37. GCC後端: Peephole optimization  以管窺天最佳化法  掃過整個IR, 看附近2 ~ n 個IR有沒有最佳化的機會  感覺起來就像是用奧步  可是如果奧步有用,那就得要好好使用  在某些時候, 這個最佳化很好用, 尤其要生數據的時候 XD ;; sub rd, rn, #1 ;; cmn rd, #1 (equivalent to cmp rd, #-1) ;; bne dest ;; subs rd, rn, #1 ;; bcs dest ((unsigned)rn >= 1) ;; This is a common looping idiom (while (n--))
  • 38. 分很多個.c file or not 分很多個.c file  為什麼要分很多檔案?  因為要讓事情變得簡單  為什麼我們在編譯程式的時候可以下 make -j24?  因為編譯器在Compile 每個 .c 的時候視為獨立個體, 彼 此之間沒有相依性  用Compiler的術語稱之為 Compilation Unit  Static Global Variable vs Non-Static Global Variable  Static: 只有這個 Compilation Unit 可以看到這個變數  Non-Static: 所有Compilation Unit 可以看到這個變數  Q: 怎麼使用別的別的 Compilation Unit 內的變數  A: 使用 extern 關鍵字. EX: extern int i;
  • 39. 深入淺出 Compilation Unit Compilation Unit  Compilation Unit 產生的限制 Internal Data Visible Data  關於沒有人使用的 Static Global External Data Reference Variable  Compiler 可以砍掉它來節省空間 Internal Function  關於沒有人使用的 Non-Static Global Variable Visible Function  Compiler 不能砍掉它  因為不能確定別的 Compilation Unit 會不會用到它 External Func Reference  Note: 如果確定沒有別的檔案 Internal Function 會使用到這個變數或函式, 請 宣告成 static
  • 40. Compilation Unit 盡可能使用區域變數 static int i; int main() int main() { { int i; for (i = 0 ; i < 10; i++) for (i = 0 ; i < 10; i++) printf ("Hello %dn", i); printf ("Hello %dn", i); } } main: main: mov r3, #0 stmfd sp!, {r4, lr} 11 stmfd sp!, {r4, lr} mov r4, #0 movw r4, #:lower16:.LANCHOR0 .L2: movt r4, #:upper16:.LANCHOR0 mov r1, r4 mov r1, r3 ldr r0, .L4 str r3, [r4, #0] add r4, r4, #1 .L2: bl printf ldr r0, .L4 cmp r4, #10 bl printf bne .L2 ldr r1, [r4, #0] ldmfd sp!, {r4, pc} add r1, r1, #1 .L4: str r1, [r4, #0] .word .LC0 cmp r1, #9 ble .L2 ldmfd sp!, {r4, pc} .L4: .word .LC0
  • 41. GCC 內建函式 (Built-in Function)  GCC 為了進行最佳化, 會辨認標準函式(因為語意有 標準規範), 如果符合條件, 就會以處理器最適合的 方式來計算  Strcpy => x86 字串處理指令  Printf => Puts, Putc  Memcpy => Block Transfer Instruction  這對開發嵌入式系統的人常造成困擾  因為開發嵌入式系統的人喜歡開發自己殘廢的printf (X)  因為開發嵌入式系統需要客製化的printf (O)  Note: 使用 -fno-builtin-XXX or -fno-builtin 來關閉這個功能
  • 42. IPO (Inter-Procedural Optimization)  Procedure / Function: 結構化程式的主軸, 增加可用 性, 減少維護成本  副程式呼叫的執行成本  準備參數: 把變數移到到特定 Register 或 push 到 Stack  呼叫副程式, 儲存和回復 Register Context  現代化程式: 有許多很小的副程式  int IsOdd(int num) { return (num & 0x1); }  最簡單的 IPO: Function inlining  簡單來說: 把整個副程式copy 一份到 caller 的程式碼去
  • 43. IPO (Inter-Procedural Optimization)  如何 Inline 一個 外部函式?  基本上 Compiler 無法做到  因為 Compiler 在編譯程式的時候不會去看別的 Compilation Unit 的內容, 不知道內容自然就沒辦法 inline 一個看不見的函式  解法: Link Time Optimization (LTO)  或叫: Whole-Program Optimization (WPO)  關鍵 Link Time Code Generation Source 1 Compiler IR 1 Linker Full IR Compiler Source 2 Compiler IR 2 Optimized Source 3 Compiler IR 3 Program
  • 44. 21世紀的另外一種選擇: LLVM  LLVM (以前稱為 Low Level Virtual Machine)  用 “現代“ C++ 寫成的 Compiler infrastructure  設計來進行全時最佳化 (compile-time, link-time, run- time, and idle-time optimizations)  LLVM 起源  2000年UIUC Vikram Adve 與 Chris Lattner 的研究發展而成  後來受到蘋果重用而大發異彩  蘋果概念股 (?)  FreeBSD 10 將使用 Clang/LLVM 為預設的編譯器
  • 45. LLVM: 全時最佳化編譯器平台 GNU Toolchain 的守備範圍  知道原始碼的結構 (O) 編譯階段  不知道變數的位址 (X)  不知道原始碼的結構 (X) 連結階段  知道變數的位址 (O)  不知道原始碼的結構 (X) 載入/安裝階段  不知道變數的位址 (X)  知道處理器有甚麼能力 (O) 執行階段 正所謂是 橫看成嶺側成峰 空閒階段 遠近高低各不同
  • 46. LLVM核心觀念: LLVM is a Compiler IR  既然 Compiler framework 就是要支援多種 Front-end 和 多種 Back-end  使用 RISC-Like 平台無關的指令 (LLVM Instruction)和資料 (Metadata) 來表達原始碼  LLVM IR 的三變化 (等價形式)  In-Memory Form: JIT  Assembly language representation: ASM format (.ll)  Bitcode representation: Object code format (.bc)  特點  Self-contained Representation 且有良好規範的 Compiler IR  可以將 Compiler 到一半的結果序列化
  • 47. LLVM 的架構 : 從原始碼到二進制 LLVM Optimizer C Front-End BitCode Stream-out LLVM bitcode Passes Optimized C++ Front-End LLVM bitcode Selection DAG Passes … Front-End Machine Passes Not in LLVM LLVM C/C++ LLVM ARM LLVM x86 Back-End Back-End Back-End In LLVM C/C++ Source ARM ASM X86 ASM Use LLVM Target ARM Object X86 Object Execution (JIT) Execution (JIT)
  • 48. LLVM Toolchain (llvm-gcc)  既然 LLVM 只是一個 Compiler-IR, 就代表 LLVM 在剛 開始的時候是沒有辦法走完整個編譯流程的  LLVM-GCC (gcc-4.2)  利用 GCC 已經存在而且眾多大眾使用的 Front-end  後來 RMS 就出來打槍這種方式 (GCC 4.3, GPLv3)  後來 Apple 就跳出來發展自己的 Front-end (Clang) C Source C Front-End Generic C++ Source C++ Front-End Gimplify Gimple LLVM Bitcode NOTE: LLVM早期的也沒有自己的 Assembler (後來有 MC) 和 Linker 來建立完整編譯流程
  • 49. LLVM Toolchain (Clang)  Clang : C, C++, Objective-C 的編譯器前端  Apple 為了取代 GCC 重新打造的 Frontend  重寫一個前端代價是非常昂貴的  其實你是 慣C? 還是 慣GCC?  GNU/Linux 世界使用了 GCC 行之有年的非標準語法  Clang 設計成模組化 Frontend : Clang C API  可以 Export AST (Ex: RenderScript 用來產生 Java 的 Binding)  可以用來實作自動完成, Refactor Tool … etc  可以用來實作靜態程式碼分析工具 (Linting tool … etc)  Clang 改善了錯誤訊息 (Expressive diagnostics )  快速編譯, 低記憶使用量, 容易學習和Hack的 Frontend
  • 50. 範例: Clang Expressive Diagnostics struct a { virtual int bar(); }; struct foo : public virtual a { }; void test(foo *P) { return P->bar() + *P; } g++-4.7 xx.cc: In function 'void test(foo*)': xx.cc:9:24: error: no match for 'operator+' in '(((a*)P) + ((sizetype)(*(long int*)(P- >foo::<anonymous>.a::_vptr.a + -32u))))->a::bar() + * P' xx.cc:9:24: error: return-statement with a value, in function returning 'void' [-fpermissive] clang-3.1 xx.cc:9:21: error: invalid operands to binary expression ('int' and 'foo') return P->bar() + *P; ~~~~~~~~ ^ ~~
  • 51. Clang/LLVM Toolchain C Source CLANG Language LLVM Bitcode C++ Source Optimizer Binding Script .. etc AST Stream-out  Clang Compiler Driver  盡可能和 GCC 相容, 遇到不認識的Option不會停下來  不需要 Cross Compiler, 本身就有 Cross Compiler 的能力 (?)  Cross Compiler != Cross Compiler Toolchian 來源: http://www.aosabook.org/en/llvm.html
  • 52. 滅諸侯,成帝業,為天下一統 LLVM 千秋萬世, 一統江湖  Q:挖賽, Clang + LLVM 那麼神, 其他人不是沒戲唱了嗎  A: 在這地球上還沒有人能統一天下, LLVM 當然也不行  Clang 之所以不用產生 Cross Compiler 的迷思  x86/64 clang 產生出來的LLVM Bitcode, 用 i686 的 llvm backend 來產生 x86 的機器碼 (-m32)  可以產生出來, 可是不會動 XD  C/C++ 不是 Portable Language, 產生出來的 Bitcode 不 Portable 不是也是很正常的事嗎  LLVM Bitcode 本身就是 Portable (X)  使用 host triple 來改變產生出來的 LLVM Bitcode (O)
  • 53. LLVM TableGen (Target Description) Instruction Encoding (For MC Assembler) Input/Output Assembly (For ASMPrinter) def ADDPS : PI<0x58, Instruction Selection Pattern (outs VR256:$dst), (For Selection DAG) (ins VR256:$src1, VR256:$src2), "addps $src2, $src1, $dst", [(set VR256:$dst, (fadd VR256:$src1, VR256:$src2))]>;  編譯器Backend 都有一些很無聊的地方, 用另外一 種語言來表示比直接寫 C/C++ 簡單好 maintain 的  GCC RTL Pattern : (MD, Machine Description file)  LLVM TableGen : (TD, Target Description file)
  • 54. 完美Compiler進化論: LLVM的多變化 Source 1 Frontend LLVM IR 1 LLVM Link IPOed executable/ Source 2 Frontend LLVM IR 2 LLVM Optimizer Share object LLVM ARM Source 3 Frontend LLVM IR 3 Backend Compile Time Link Time Source 1 Frontend LLVM IR 1 LLVM Link LLVM ARM Backend Source 2 Frontend LLVM IR 2 LLVM Optimizer Processor Specific executable/ Source 3 Frontend LLVM IR 3 Share object Compile Time Link Time Load /Installation Time LLVM IR PGOed/ Runtime Opted LLVM Optimizer LLVM ARM Backend executable/ Share object Profile Data Idle Time Load Time
  • 55. LLVM 的發展  史記: 項羽本紀第七  太史公曰:吾聞之周生曰「舜目蓋重瞳子」,又聞項羽亦重瞳子。 羽豈其苗裔邪?何興之暴也!  LLVM 也有兩個 L, 加上一家可以掌握全局垂直整合的公司在後面力 挺, LLVM 也是銳不可擋  最美好的時代, 最壞得的時代  LLVM Bitcode 用來當傳遞格式還有很多問題  Binary Compatibility 最為人所詬病  http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon- 20120521/143197.html  CASE STUDY: OpenCL SPIR  The OpenCL Khronos group proposes to extend LLVM to be the standard OpenCL IR (called SPIR)  http://www.khronos.org/registry/cl/specs/spir_spec-1.0-provisional.pdf  Khronos SPIR For OpenCL Brings Binary Compatibility  http://www.phoronix.com/scan.php?page=news_item&px=MTE4MzM
  • 56. 相信你的編譯器  Linux-Kongress 2009: Source Code Optimization  http://www.linux-kongress.org/2009/slides/compiler_survey_ felix_von_leitner.pdf  Q: x86 中把一個暫存器清成0  mov $0,%eax  and $0,%eax  sub %eax,%eax  xor %eax,%eax  哪道指令最好?
  • 57. 相信你的編譯器: 解答  x86 的指令是可變長度的:  b8 00 00 00 00 mov $0,%eax  83 e0 00 and $0,%eax  29 c0 sub %eax,%eax  31 c0 xor %eax,%eax  So, sub or xor? Turns out, both produce a false dependency on %eax. But CPUs know to ignore it for xor.
  • 58. References  http://www.redhat.com/magazine/002dec04/features/gcc/  http://gcc.gnu.org/onlinedocs/gcc  http://en.wikipedia.org/wiki/Interprocedural_optimization  http://en.wikipedia.org/wiki/Link-time_optimization  http://llvm.org/  http://www.aosabook.org/en/llvm.html