LLVM的概念
日常開發(fā)過程我們的開發(fā)工具或多或少都跟LLVM
扯得上一點(diǎn)關(guān)系,那么什么是LLVM
呢筷畦?它有什么作用呢继效?首先我們需要明白兩個(gè)概念解釋型語言
、編譯型語言
训措。解釋型語言
:當(dāng)它讀到當(dāng)前代碼就立即執(zhí)行如python
伪节。編譯型語言
:他需要先把它翻譯成CPU
可以讀懂的二進(jìn)制文件才可以執(zhí)行。LLVM
也稱為架構(gòu)編譯器隙弛,它是用C++編寫的架馋,主要作用是用于優(yōu)化任意程序編寫的程序編譯時(shí)間????????????????????????????????????、鏈接時(shí)間全闷、運(yùn)行時(shí)間和空閑時(shí)間叉寂,對開發(fā)者開放并兼容已有的腳本。
編譯器設(shè)計(jì)
上圖為傳統(tǒng)的編譯器設(shè)計(jì)总珠,它是一種前后端分離的一種模式屏鳍。
編譯器前端(Frontend)
:解析源代碼。它會進(jìn)行詞法分析局服、語法分析钓瞭、語義分析,源代碼錯(cuò)誤檢查淫奔,并構(gòu)建抽象語法樹(AST)山涡,LLVM
的前端還會生成中間代碼(IR)。優(yōu)化器(Optimizer)
:負(fù)責(zé)各種優(yōu)化,改善代碼的運(yùn)行時(shí)間鸭丛,如優(yōu)化代碼中的冗余計(jì)算竞穷。后端(Backend)/代碼生成器(CodeGenerator)
:將優(yōu)化后的代碼轉(zhuǎn)化為二進(jìn)制文件并映射到目標(biāo)指令集。
iOS的編譯器架構(gòu)
OC鳞溉、C瘾带、C++使用的編譯器前端是Clang,Swift使用的編譯器前端是Swift熟菲,后端都是用的LLVM看政。
LLVM的優(yōu)點(diǎn)
LLVM的設(shè)計(jì),使用通用的代碼表示形式IR抄罕,它是用來在編譯器中表示代碼的形式允蚣。LLVM可以為任何任何語言獨(dú)立的編寫前端,并為任意的硬件架構(gòu)編寫后端贞绵。
Clang
Clang是LLVM項(xiàng)目中的一個(gè)子項(xiàng)目厉萝。它是一個(gè)輕量級的編譯器,它是負(fù)責(zé)編譯C榨崩、C++谴垫, OC語言的編譯器,它在LLVM架構(gòu)中的編譯器的前端母蛛。
終端輸入open /usr/bin
可以查看到編譯器Clang
編譯流程分析
-
首先創(chuàng)建一個(gè).m文件翩剪,并cd到當(dāng)前文件路徑
- 終端輸入
clang -ccc-print-phases main.m
,結(jié)果終端會打印如下
0
:輸入文件彩郊,找到源文件前弯。
1
:預(yù)處理階段,處理宏的替換秫逝,頭文件的引入恕出。
2
:編譯階段,詞法分析违帆、語法分析浙巫、最終生成IR。
3
:后端:此階段LLVM通過一個(gè)一個(gè)的pass去優(yōu)化刷后,最終生成匯編代碼的畴。
4
:生成目標(biāo)文件。
5
:鏈接尝胆,鏈接需要的的動靜態(tài)庫丧裁,生成可執(zhí)行文件。
6
:通過不同的架構(gòu)生成對應(yīng)的可執(zhí)行文件含衔。
預(yù)處理
終端執(zhí)行clang -E main.m
執(zhí)行完成后煎娇,我們可以看到頭文件被導(dǎo)入和宏被替換了二庵。
編譯
什么叫詞法分析
呢?預(yù)處理階段會將代碼切成一個(gè)一個(gè)的token逊桦,如括號眨猎、等號、字符串等,這個(gè)過程稱為詞法分析
。
什么叫
語法分析
呢巴席?語法分析
在詞法分析之后疼进,它主要是驗(yàn)證語法是否正確。在詞法的基礎(chǔ)之上把單詞序號合成語法短語信殊,如程序炬称,表達(dá)式等,然后將所有的節(jié)點(diǎn)組成語法樹(AST)涡拘。它主要分析程序在結(jié)構(gòu)上是否正確玲躯。終端輸入
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
輸出結(jié)果如下圖
生成IR(intermediate representation)
完成上述步驟代碼生成器會將語法樹自上到下逐步翻譯成IR代碼。
終端輸入clang -S -fobjc-arc -emit-llvm main.m
可以查看IR代碼
執(zhí)行完成后可以發(fā)現(xiàn)目錄下生成了一個(gè).ll的文件
oc代碼在這一步會進(jìn)行runtime的橋接鳄乏,property合成跷车,ARC處理
- IR的基本語法
@
:全局標(biāo)識
%
:????????局部標(biāo)識
alloca
:????????開辟內(nèi)存空間
align
: ????????內(nèi)存對齊
i32
:32個(gè)??bit??,4個(gè)字節(jié)
store
:????????寫入內(nèi)存
load
:讀取數(shù)據(jù)
call
:調(diào)用函數(shù)
ret
:????返回
IR的優(yōu)化
LLVM的優(yōu)化級別分別是-O0 橱野、-O1朽缴、-O2、-O3水援、-Os
終端指令clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
bitCode
開啟bitcode后xcode會對代碼進(jìn)一步優(yōu)化密强,并生成.bc的中間代碼。
終端指令clang -emit-llvm -c main.ll -o main.bc
可以優(yōu)化IR代碼生成.bc代碼
生成匯編代碼
終端指令clang -S -fobjc-arc main.bc -o main.s
或clang -S -fobjc-arc main.ll -o main.s
可以將將.bc或者.ll代碼生成匯編代碼蜗元。當(dāng)然生成的匯編代碼也可以通過終端指令clang -Os -S -fobjc-arc main.m -o main.s
進(jìn)一步優(yōu)化
生成目標(biāo)文件
目標(biāo)文件的生成或渤,是由匯編器以匯編代碼作為輸入,將匯編代碼轉(zhuǎn)化成機(jī)器代碼奕扣,最后輸出成目標(biāo)文件薪鹦。
終端指令clang -fmodules -c main.s -o main.o
可以將匯編文件輸出成目標(biāo)文件。
main.o文件中的符號可以通過nm命令查看
終端指令xcrun nm -nm main.o
undefined
: ????????????????????????????表示在當(dāng)前文件暫時(shí)找不到符號_printf成畦。external
:表示這個(gè)符號外部可以訪問距芬。
生成可執(zhí)行文件
連接器最終將編譯生成的.o文件和.dylib.a文件,生成mach-o文件循帐。
終端指令clang main.o -o main
同理我們也可以通過nm查看鏈接之后的可執(zhí)行文件的符號框仔。
終端指令xcrun nm -nm main
總結(jié)
- 編譯流程:
輸入代碼->展開預(yù)處理->詞法分析(token)->語法分析 ->生成IR ->IR優(yōu)化 ->生成匯編代碼 ->生成目標(biāo)文件 ->鏈接動靜態(tài)庫生成可執(zhí)行文件。 -
typedef
:不做預(yù)處理拄养,不是預(yù)處理指令离斩。 - 優(yōu)化等級不是越高越好银舱,太高會把有用代碼優(yōu)化掉。
-
.o
文件不能執(zhí)行跛梗,需要鏈接外部庫寻馏,鏈接只是打標(biāo)記。 -
LLVM
優(yōu)點(diǎn):前后端分離核偿,擴(kuò)展性非常強(qiáng)诚欠。 -
LLVM
會影響編譯速度,優(yōu)化可執(zhí)行文件可以提高編譯速度漾岳。 - 不同的節(jié)點(diǎn)上轰绵,還能進(jìn)行優(yōu)化。