59
How Compiler Works FROM SOURCE TO BINARY ソースからバイナリへ 從原始碼到二進制 Von Quelle Zu Binären De source au binaire Desde fuente a binario Binarium ut a fonte From source to binary Luse Cheng Jim Huang March 31, 2011 / 北科大 Luse Cheng Sep 13, 2012 / 新竹碼農

Hcsm lect-20120913

Embed Size (px)

DESCRIPTION

新竹碼農: 20120913 From source to binary

Citation preview

Page 1: Hcsm lect-20120913

How Compiler Works

FROM SOURCE TO BINARY

ソースからバイナリへ

從原始碼到二進制

Von Quelle Zu Binären

De source au binaire

Desde fuente a binario

Binarium ut a fonte

From source to binary

Luse ChengJim HuangMarch 31, 2011 / 北科大

Luse ChengSep 13, 2012 / 新竹碼農

Page 2: Hcsm lect-20120913

自我介紹

經歷 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 工程師

Page 3: Hcsm lect-20120913

關於今天的演講

摘要

簡短的介紹從原始碼到二進制的流程

三大步驟

Compiler Driver

從原始碼到二進制的重要推手: 編譯器

課本中的編譯器

真實世界的編譯器

GCC

LLVM

Page 4: Hcsm lect-20120913

春秋:微言大義

隱公元年: 夏,五月,鄭伯克段於鄢

短短幾個字,影含著諸多含意 段不弟,故不言弟

如二君,故曰克

稱鄭伯,譏失教也

為什麼要了解從原始碼到二進制

電腦科學所有的問題都可以用另外一層的抽象化來解決 (All problems in computer science can be solved by another level of indirection)

了解背後的運作原理, 破解微言大義

Page 5: Hcsm lect-20120913

微言大義:現代Linux環境, 程式啟動流程

Shell

eglibc

kernel

execve

./hello

SYS_execve

驗證執行檔 啟動loader

ld-linux.so

libc_start_main

hello

main

exit

SYS_exit

棄置Process

動態連結器

配置Process

動態連結靜態連結

Page 6: Hcsm lect-20120913

從原始碼到二進制: 常見的誤解

Source Code

Compiler (gcc, llvm)

HDL Source

Design Compiler (DC)RTL Compiler (RC)

Assembly Netlist

SelectingInstruction

TechnologyMapping

軟體 硬體

從原始碼到二進制,其實不只需要 Compiler

真相: 需要很多 Tool 同心協力一起完成 (Toolchain)

真正的Compiler 其實只做下面的事…

A compiler is a computer program (or set of programs)that transforms source code written in a programming language (the source language) into another computer language (the target language, often having a binary form known as object code)

source : http://en.wikipedia.org/wiki/Compiler

Page 7: Hcsm lect-20120913

Executable: hello 執行檔

Linker (LD) 連結器

Object File: hello.o 目的檔

Assembler (AS) 組譯器

Assembly File: hello.s 組合語言

Compiler (CC1) 編譯器

C program: hello.c 原始碼

編譯Hello world程式的流程

編譯

組譯

連結

三大步驟

Page 8: Hcsm lect-20120913

圖解簡易完整編譯流程

int main() {printf (“Hello World”);

}

int printf (const char* fmt, …);

main:LDR R1, [R2 + 12]BAL printf

#include <stdio.h>int main() {printf (“Hello World”);

}

Preprocess Compile

Assemble

Main:EC 00 00 12F0 ?? ?? ??

Linking0x2000 <Main>:

EC 00 00 12F0 00 10 00

0x1000 <printf>:EC 00 00 12…

stdio.h

libc.a

Page 9: Hcsm lect-20120913

常見的誤解: GCC是個C 編譯器答案: GCC是一個Compiler Driver

gcc

cc1

as

cpp

collect2

Preprocessed source (.i or .ii)

Asm (.s)

Reloadable (.o)

Binary (Executable)

library

crt*

link script

Source (.c and .h)

gcc

binutils

glibc

ld

GCC 這個執行檔, 可以當Compiler, 也能當 Preprocessor, 也能當 Assembler, 更能當 Linker

Page 10: Hcsm lect-20120913

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

Page 11: Hcsm lect-20120913

如果有人想當你的程式碼那你怎樣才能做她的編譯器 (Compiler) ?

什麼是編譯器? Source Language ->

Target Language

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

High Level Language

Optimized Low Level Language

Compiler

Page 12: Hcsm lect-20120913

編譯器小歷史:

First compiler (1952) 第一個編譯器 A-0, Grace Murray Hopper

First complete compiler (1957) 第一個完整的編譯器 Fortran, John Backus (18-man years)

First multi-arch compiler (1960) 第一個多平台編譯器 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

Grace Hopper, inventor ofA-0, COBOL, and theterm “compiler.”

Page 13: Hcsm lect-20120913

先有雞還是先有蛋

先有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的編譯器

Page 14: Hcsm lect-20120913

編譯器的架構 (Textbook)

屠龍刀: Syntax Directed Translator

原始碼

字彙分析 (正規語言)

語法分析 (Context-Free Grammar)

語法樹

語意分析 (Type Checking ..etc)

最佳化

中間語言

目標語言

Page 15: Hcsm lect-20120913

編譯器技術=屠龍之技

莊子: 列禦寇第三十二

朱泙漫學屠龍於支離益,單千金之家,三年技成而無所用其巧。

用白話說就是有個人花了很多錢和時間學了屠龍

技能, 後來發現沒有龍可以殺

不過隨著硬體的發展,軟體的複雜度提升,這個世界上的龍也越來越多了

Page 16: Hcsm lect-20120913

while (y < z) {int x = a + b;y += x;

}

原始碼

字彙分析 (正規語言)

語法分析 (Context-Free Grammar)

語法樹

語意分析 (Type Checking ..etc)

最佳化

中間語言

目標語言

T_While

T_LeftParen

T_Identifier y

T_Less

T_Identifier z

T_RightParen

T_OpenBrace

T_Int

T_Identifier x

T_Assign

T_Identifier a

T_Plus

T_Identifier b

T_Semicolon

T_Identifier y

T_PlusAssign

T_Identifier x

T_Semicolon

T_CloseBrace

How Compiler Works

http://www.stanford.edu/class/cs143/

Page 17: Hcsm lect-20120913

原始碼

字彙分析 (正規語言)

語法分析 (Context-Free Grammar)

語法樹

語意分析 (Type Checking ..etc)

最佳化

中間語言

目標語言

while (y < z) {int x = a + b;y += x;

} How Compiler Works

http://www.stanford.edu/class/cs143/

Page 18: Hcsm lect-20120913

原始碼

字彙分析 (正規語言)

語法分析 (Context-Free Grammar)

語法樹

語意分析 (Type Checking ..etc)

最佳化

中間語言

目標語言

while (y < z) {int x = a + b;y += x;

} How Compiler Works

http://www.stanford.edu/class/cs143/

Page 19: Hcsm lect-20120913

原始碼

字彙分析 (正規語言)

語法分析 (Context-Free Grammar)

語法樹

語意分析 (Type Checking ..etc)

最佳化

中間語言

目標語言

while (y < z) {int x = a + b;y += x;

} How Compiler Works

http://www.stanford.edu/class/cs143/

Loop: x = a + by = x + y_t1 = y < zif _t1 goto Loop

x = a + bLoop: y = x + y

_t1 = y < zif _t1 goto Loop

add $1, $2, $3loop: add $4, $1, $4

slt $6, $1, $5beq $6, loop

Page 20: Hcsm lect-20120913

真實世界中的編譯器GCC, the GNU Compiler Collection

海灣合作委員會 (X)

GNU Compiler Collection (O)

目前最多(*)使用者使用的Compiler(?)

Page 21: Hcsm lect-20120913

真實世界中的編譯器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

Page 22: Hcsm lect-20120913

GCC 的架構

課本終究還是課本

GCC 的特性與挑戰: GCC 支援多種語言 (前端)

GCC 支援多種處理器 (後端)

Syntax Directed Translator 程式的語法樹結構將無法和處理器架構脫鉤

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

所以 GCC 引入中間層最佳化 (編譯器相關最佳化)

int main(){

return 0;IamDeadCode();

}

Page 23: Hcsm lect-20120913

GCC 的解決之道 (GCC 4.0 以後)

語言相關

平台相關

編譯器相關

C Front-End

C++ Front-EndGeneric

C Source

C++ Source

Gimple

Gimplify

Tree SSAOptimizer

RTLRTL

OptimizerTarget ASM

Code Generator IR

Page 24: Hcsm lect-20120913

資料流程分析 (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

Page 25: Hcsm lect-20120913

Code Motion & Pointer Aliasing

C 語言中阻擋 Compiler 最佳化的一大原因

Pointer Aliasing: 兩個不同的指標指到同一個記憶體位置

while (y < z) {int x = a + b;y += x;

}

void foo(int *a, int *b, int *y, int z) {while (*y < z) {

int x = *a + *b;*y += x;

}}

int t1 = a + b;while (y < z) {y += t1;

}

Q: How about this?

A: Compiler 不能把*a + *b 移到迴圈外

因為不確定使用者會不會這樣使用: foo(&num, &num, &num, 5566);

這樣 a, b, y 互為別名, *y 的值改變會使 *a , *b 改變, 故不能提出到迴圈外

Page 26: Hcsm lect-20120913

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

Page 27: Hcsm lect-20120913

Static single assignment, SSA Form

Example: (From Wiki)

INPUT: y = 1, y =2, x = y

SSA: y1 = 1, y2 = 2, x1 = y2

什麼是 SSA Form Social Security Administration (X) 靜態單賦值形式 (O) 静的単一代入 (O)

用白話講, 在這個表示法當中每個被 Assign 的變數只會出現一次

作法 1. 每個被Assign的變數給一個Version number 2. 使用 Φ functions

Diego Novillo, GCC Internals - IRs

Page 28: Hcsm lect-20120913

使用 SSA 可以加強/加快以下的最佳化

Constant propagation

Sparse conditional constant propagation

Dead code elimination

Global value numbering

Partial redundancy elimination

Strength reduction

Register allocation

Page 29: Hcsm lect-20120913

SSA @ Constant propagation (常數傳遞)

用了SSA後每次 Constant propagation 都考100分

EX: GCC-3.x GCC-4.x Example

main:mov r0, #0bx lr

int main(){int a = 11,b = 13, c = 5566;int i, result;for (i = 0 ; i < 10000 ; i++)result = (a*b + i) / (a*c);

return result;}

main:…mov r4, #0

.L5:mov r1, #61184add r0, r4, #143add r1, r1, #42add r4, r4, #1bl __divsi3cmp r4, r5ble .L5…

GCC-4.x (SSA)

GCC-3.x (No-SSA)

Page 30: Hcsm lect-20120913

GCC前端: Source -> AST -> Generic

GCC C/C++ frontend BISON (3.x) Hand-written Recursive descent parser (4.x)

C++ in 2004 C and Objective-C in 2006

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

Abstract Syntax Tree (AST) 抽象語法樹 剖析樹 + 語意 EX: b = (a - 56) / c

=

b /

c-

a 56

b = (a - 56) / c

Page 31: Hcsm lect-20120913

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

Page 32: Hcsm lect-20120913

GCC後端: Register Transfer Language (RTL)

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

(set (reg:SI 60 [b])(plus:SI (reg:SI 61 [a])

(const_int -56 [0xffffffc8])))

b = a - 56

Page 33: Hcsm lect-20120913

GCC後端: RTL Patten Match Engine

(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""@addu\t%0,%1,%2addiu\t%0,%1,%2"

[(set_attr "type" "arith")(set_attr "mode" "si")])

MIPS.md

d代表是RegisterQ代表是整數

指令的屬性(用於管線排程)

指令限制(非MIPS16才可使用)addiu $2, $3, -56

b = a - 56

(set (reg:SI 60 [b]) (plus:SI (reg:SI 61 [a])

(const_int -56 [0xffffffc8])))

Page 34: Hcsm lect-20120913

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 的做法

Page 35: Hcsm lect-20120913

GCC後端 :管線排程

IF ID EX ME WBlw $8, alw $9, b

mul $10,$8,$8add $11,$9,$10

Clock 0Clock 1Clock 2Clock 3Clock 4Clock 5Clock 6Clock 7Clock 8

lw $12,cadd $13,$12,$0

以經典課本的五級Pipeline 為例子

lw $8, alw $9, b

lw $12,cmul $10,$8,$8

add $13,$12,$0

IF ID EX ME WB

Page 36: Hcsm lect-20120913

GCC後端 :管線排程 (續)

GCC use finite state automaton (FA) based pipeline scheduling model

Page 37: Hcsm lect-20120913

GCC後端: Peephole optimization

以管窺天最佳化法

掃過整個IR, 看附近2 ~ n 個IR有沒有最佳化的機會

感覺起來就像是用奧步

可是如果奧步有用,那就得要好好使用

在某些時候, 這個最佳化很好用, 尤其要生數據的時候 XD

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

;; This is a common looping idiom (while (n--))

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

Page 38: Hcsm lect-20120913

分很多個.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;

Page 39: Hcsm lect-20120913

深入淺出Compilation UnitCompilation Unit

Internal DataVisible Data

Internal Function

Visible Function

Internal Function

External Data Reference

External Func Reference

Compilation Unit 產生的限制

關於沒有人使用的 Static Global Variable

Compiler 可以砍掉它來節省空間

關於沒有人使用的 Non-Static Global Variable

Compiler 不能砍掉它

因為不能確定別的 Compilation Unit 會不會用到它

Note: 如果確定沒有別的檔案

會使用到這個變數或函式, 請宣告成 static

Page 40: Hcsm lect-20120913

Compilation Unit

盡可能使用區域變數

main:mov r3, #0stmfd sp!, {r4, lr}movw r4, #:lower16:.LANCHOR0movt r4, #:upper16:.LANCHOR0mov r1, r3str r3, [r4, #0]

.L2:ldr r0, .L4bl printfldr r1, [r4, #0]add r1, r1, #1str r1, [r4, #0]cmp r1, #9ble .L2ldmfd sp!, {r4, pc}

.L4:.word .LC0

static int i;int main(){for (i = 0 ; i < 10; i++)

printf ("Hello %d\n", i);}

int main(){int i;for (i = 0 ; i < 10; i++)

printf ("Hello %d\n", i);}

main:stmfd sp!, {r4, lr}mov r4, #0

.L2:mov r1, r4ldr r0, .L4add r4, r4, #1bl printfcmp r4, #10bne .L2ldmfd sp!, {r4, pc}

.L4:.word .LC0

11

Page 41: Hcsm lect-20120913

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 來關閉這個功能

Page 42: Hcsm lect-20120913

IPO (Inter-Procedural Optimization)

Procedure / Function: 結構化程式的主軸, 增加可用性, 減少維護成本

副程式呼叫的執行成本

準備參數: 把變數移到到特定 Register 或 push 到 Stack

呼叫副程式, 儲存和回復 Register Context

現代化程式: 有許多很小的副程式

int IsOdd(int num) {return (num & 0x1);

}

最簡單的 IPO: Function inlining

簡單來說: 把整個副程式copy 一份到 caller 的程式碼去

Page 43: Hcsm lect-20120913

IPO (Inter-Procedural Optimization)

如何 Inline 一個外部函式?

基本上 Compiler 無法做到

因為 Compiler 在編譯程式的時候不會去看別的Compilation Unit 的內容, 不知道內容自然就沒辦法 inline 一個看不見的函式

解法: Link Time Optimization (LTO)

或叫: Whole-Program Optimization (WPO)

關鍵 Link Time Code Generation

Source 1

Source 2

Source 3

Compiler

Compiler

Compiler

IR 1

IR 2

IR 3

Linker Full IR Compiler

Optimized Program

Page 44: Hcsm lect-20120913

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 為預設的編譯器

Page 45: Hcsm lect-20120913

LLVM:全時最佳化編譯器平台

空閒階段

執行階段

載入/安裝階段

連結階段

編譯階段

正所謂是橫看成嶺側成峰遠近高低各不同

知道原始碼的結構 (O)

不知道變數的位址 (X)

不知道原始碼的結構 (X)

知道變數的位址 (O)

不知道原始碼的結構 (X)

不知道變數的位址 (X)

知道處理器有甚麼能力 (O)

GNU Toolchain 的守備範圍

Page 46: Hcsm lect-20120913

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 到一半的結果序列化

Page 47: Hcsm lect-20120913

LLVM 的架構 :從原始碼到二進制

C Front-End

C++ Front-End

LLVM bitcode

… Front-End

Stream-outOptimized

LLVM bitcode

LLVM x86Back-End

LLVM ARMBack-End

X86 ASM

X86 Object

ARM ASM

ARM Object

Execution (JIT)Execution (JIT)

In LLVM

Not in LLVM

LLVM Optimizer

BitCodePasses

Selection DAGPasses

MachinePasses

LLVM C/C++Back-End

C/C++ Source

Use LLVM Target

Page 48: Hcsm lect-20120913

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 Front-End

C++ Front-EndGeneric

C Source

C++ Source

Gimple

Gimplify

LLVM Bitcode

NOTE: LLVM早期的也沒有自己的 Assembler (後來有 MC) 和 Linker 來建立完整編譯流程

Page 49: Hcsm lect-20120913

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

Page 50: Hcsm lect-20120913

範例: Clang Expressive Diagnostics

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]

struct a {virtual int bar();

};struct foo : public virtual a {};void test(foo *P) {

return P->bar() + *P;}

xx.cc:9:21: error: invalid operands to binary expression ('int' and 'foo')return P->bar() + *P;

~~~~~~~~ ^ ~~

g++-4.7

clang-3.1

Page 51: Hcsm lect-20120913

Clang/LLVM Toolchain

Clang Compiler Driver 盡可能和 GCC 相容, 遇到不認識的Option不會停下來

不需要 Cross Compiler, 本身就有 Cross Compiler 的能力 (?) Cross Compiler != Cross Compiler Toolchian

CLANGLanguageOptimizer

C Source

C++ Source

LLVM Bitcode

AST Stream-outBinding Script .. etc

來源: http://www.aosabook.org/en/llvm.html

Page 52: Hcsm lect-20120913

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)

滅諸侯,成帝業,為天下一統

Page 53: Hcsm lect-20120913

LLVM TableGen (Target Description)

編譯器Backend 都有一些很無聊的地方, 用另外一種語言來表示比直接寫 C/C++ 簡單好 maintain 的

GCC RTL Pattern : (MD, Machine Description file)

LLVM TableGen : (TD, Target Description file)

def ADDPS : PI<0x58,(outs VR256:$dst),(ins VR256:$src1, VR256:$src2),"addps $src2, $src1, $dst",[(set VR256:$dst, (fadd VR256:$src1, VR256:$src2))]>;

Instruction Encoding (For MC Assembler)

Input/Output

Assembly (For ASMPrinter)

Instruction Selection Pattern(For Selection DAG)

Page 54: Hcsm lect-20120913

完美Compiler進化論: LLVM的多變化

Source 1

Source 2

Source 3

Frontend

Frontend

Frontend

LLVM IR 1

LLVM IR 2

LLVM IR 3

LLVM Link

LLVM Optimizer

LLVM ARM Backend

IPOedexecutable/Share object

Compile Time Link Time

Source 1

Source 2

Source 3

Frontend

Frontend

Frontend

LLVM IR 1

LLVM IR 2

LLVM IR 3

LLVM Link

LLVM Optimizer

LLVM ARM Backend

Processor Specificexecutable/Share object

Compile Time Link Time Load /Installation Time

LLVM IR

Idle Time

LLVM OptimizerProfile Data

LLVM ARM BackendPGOed/ Runtime Optedexecutable/ Share object

Load Time

Page 55: Hcsm lect-20120913

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

Page 57: Hcsm lect-20120913

相信你的編譯器: 解答

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.

Page 59: Hcsm lect-20120913

Any Question ?