一、 概述
LLVM是架構(gòu)編譯器的框架系統(tǒng),由C++編寫而成。由于優(yōu)化以任意程序語言編寫的程序的編譯時間(complie-time)、鏈接時間(link-time)、運行時間(run-time)以及空閑時間(idle-time)。對開發(fā)者保持開放,并兼容已有的腳本。
二、LLVM設(shè)計結(jié)構(gòu)
傳統(tǒng)的LLVM設(shè)計結(jié)構(gòu)如下:

分為 源碼、前端、優(yōu)化器、后端、以及機器識別語言等幾部分。
2.1 前端編譯器(Frontend)
前端編譯器的任務(wù)是解析源代碼。它會進行相應(yīng)的:詞法分析、語法分析、語義分析。檢查源代碼是否存在語法錯誤,然后構(gòu)建抽象語法樹(Abstract Syntax Tree,AST),LLVM的前端編譯器還會生成中間代碼((intermediate representation,IR).
2.2 優(yōu)化器(optimizer)
優(yōu)化器負責(zé)進行各種優(yōu)化,改善代碼的運行時間。例如消除冗余計算等。
2.3后端(backend)
將代碼映射帶目標(biāo)指令集,生成機器語言,并且進行機器相關(guān)的代碼優(yōu)化。
2.4 iOS的編譯架構(gòu)
Objective C、C、C++使用的編譯器前端是clang,Swift 的編譯器前端是swift,后端都是LLVM.

2.5 LLVM的設(shè)計
當(dāng)編譯器決定支持多種語言或多種硬件架構(gòu)時,LLVM的最重要的時刻就來了。其他的編譯器如GCC,它方法非誠成功,但是由于它是作為整體程序設(shè)計的,因此它的用途受到了很大的限制。
LLVM設(shè)計最重要的地方是、使用通過代碼形式(IR)、是用來在編譯器中表示代碼的形式。所以LLVM可以為任何編程語言獨立編寫前端,并且可以為任意硬件架構(gòu)獨立編寫后端。

2.6 Clang
clang是LLVM的項目中的一個子項目,它是基于LLVM架構(gòu)的輕量級編譯器,誕生之初是為咯替換GCC,提供更快的編譯速度,它屬于LLVM的前端編譯器。
三,LLVM的編譯過程
3.1 LLVM的編譯步驟
現(xiàn)在我們新建一個空項目,創(chuàng)建一個命令行的項目,然后寫一些相關(guān)的代碼
#import <stdio.h>
#define C 30
int main(int argc, const char * argv[]) {
int a = 10;
int b = 20;
printf("%d \n",a + b + C);
return 0;
}
然后我們在該工程的目錄下創(chuàng)建終端命令:
然后我們看看LLVM進行編譯的過程中要執(zhí)行的相關(guān)步驟命令
clang -ccc-print-phases main.m
執(zhí)行結(jié)果如下:
0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image
- step1 :輸入文件,找到源文件
- stem2:預(yù)處理階段,這個過程包括宏定義的替換,頭文件的導(dǎo)入和展開。
- step3:編譯階段,進行詞法分析、語法分析、檢測語法是否正確生成IR.
- step4:后端,這里L(fēng)LVM會通過一個一個的Pass去優(yōu)化,每個Pass做一些事情,最終生成匯編代碼。
- step5 生成目標(biāo)文件,
- step6 鏈接,鏈接需要的動態(tài)庫和靜態(tài)庫,生成可執(zhí)行文件。
- step7 通過不同的架構(gòu),生成對應(yīng)的可執(zhí)行文件
3.2 預(yù)處理階段(preprocessor)
我們執(zhí)行以下命令
clang -E main.m -o main2.m
我們會看到和main文件同級目錄下多了一個main2.m的文件,我們打開改文件會發(fā)現(xiàn)和之前的main有所不一樣了,不一樣之處如下圖

我們看到預(yù)編譯階段,文件的行數(shù)變得多了很多,所以并且之處的宏定義C已經(jīng)替換為30。這就是預(yù)編譯過程的事情,
3.3 詞法分析
預(yù)處理階段過后就會進行詞法分析,這里會把每個代碼切成一個一個的token,這里我們只需要執(zhí)行以下命令
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
這樣 在終端就會生成一行一行的代碼

這樣,我們程序就被切成一個一個的token 包括空格和標(biāo)點符號,以及具體對應(yīng)的代碼位置都被token標(biāo)注的清清楚楚。
3.4 生成語法樹
由上一步生成的語法樹,我們只需要執(zhí)行命令
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
語法樹的生成如下

3.6 生成中間代碼
對于以上生成中間的代碼。我們需要執(zhí)行命令
clang -S -fobjc-arc -emit-llvm main.m
執(zhí)行完成過后我們能看到和main.m文件同級的目錄中多了的main.ll的文件,這就是IR的格式文件
我們用WebStorm打開改文件如圖

相關(guān)的關(guān)鍵之解析
@:全局標(biāo)識
% 局部標(biāo)識
alloca 開辟內(nèi)存空間
align 字節(jié)對齊
store 寫入內(nèi)存load 肚內(nèi)存
call 調(diào)用函數(shù)
ret 返回值
i32 int 4個字節(jié)
3.7 IR的優(yōu)化方式
LLVM的優(yōu)化級別分別是-O0、-O1、-O2、-O3、-Os(第一個字母必須大寫)
執(zhí)行命令
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
3.8 bitCode
通過編譯生成的中間代碼生成bitCode執(zhí)行指令
clang -emit-llvm -c main.ll -o main.bc
3.9 生成匯編代碼
通過生成的bitCode 或者中間代碼生成匯編代碼如下
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
生成的匯編代碼也可以進行優(yōu)化
clang -Os -S -fobjc-arc main.m -o main.s
3.10 生成目標(biāo)文件
生成目標(biāo)文件的執(zhí)行指令
clang -fmodules -c main.s -o main.o
通過nm指令,查看main.o文件
$xcrun nm -nm main.o
3.11 生成可執(zhí)行文件(鏈接)
命令如下
clang main.o -o main
再次可以通過nm命令查看,可以看到完全不一樣的結(jié)果。
四、總結(jié)
以上就是對LLVM的初探的整個過程,雖然有些內(nèi)容自己還沒去驗證,但是大致流程就這樣,這也是先記錄然后自我學(xué)習(xí)的一個過程,希望在每天的學(xué)習(xí)過程中進步一點點吧,有什么不足的地方還請多多指正。覺得有用的就借鑒一下,如果覺得沒用的請越過。