LLVM
LLVM是架構編譯器
(compiler)的框架系統,由C++編寫完成,用于優化
已任意程序編寫的程序的編譯時間
(compile-time)、鏈接時間
(link-time)、運行時間
(run-time)以及空閑時間
(idle-time),對開發者保持開放,并兼容已有腳本
傳統編譯器設計
編譯器前端(Frontend):負責
解析源代碼
,它會進行詞法分析
、語法分析、語義分析和檢查源代碼是否存在錯誤,然后構建抽象語法樹(Abstract Syntax Tree,AST)
,LLVM
會生成中間代碼
(intermediate representationIR
)優化器(Optimizer):負責進行各種
優化
,改善代碼的運行時間
,例如消除冗余計算等后端(Backend)/代碼生成器(CodeGenerator):將
代碼映射到目標指令集
。生成機器語言
,并進行機器相關的代碼優化
iOS的編譯器架構
OC/C/C++
使用的編譯器前端是Clang
,swift前端是Swift
,后端都是LLVM
,Clang
會生成中間代碼
(intermediate representation IR
)
LLVM設計
使用通用的代碼表示形式(IR)
,用來在編譯器中表示代碼的形式,所有LLVM可以為任何編程語言獨立編寫前端,并且可以為任意硬件架構獨立編寫后端
Clang簡介
Clang
是LLVM項目中的子項目,基于LLVM架構的輕量級編譯器
,誕生之初是為了替代GCC,提高更快的編譯速度。它是負責編譯C、C++、Object-C語言的編譯器,在整個LLVM架構中屬于編譯器前端
編譯流程
- 單獨新建一個
main.m
文件
int test(int a,int b){
return a + b + 3;
}
int main(int argc, const char * argv[]) {
int a = test(1, 2);
printf("%d",a);
return 0;[圖片上傳中...(image.png-1984f6-1624280414052-0)]
}
- 通過命令
clang -ccc-print-phases main.m
LLVM編譯流程
//輸入文件:找到源文件
+- 0: input, "main.m", objective-c
//預處理階段:處理包括宏的替換,頭文件的導入
+- 1: preprocessor, {0}, objective-c-cpp-output
//編譯階段:進行詞法、語法分析,檢測語法是否正確,最終生成IR
+- 2: compiler, {1}, ir
//后端:LLVM會通過一個一個的Pass去優化,每個Pass做些事情,最終生成匯編代碼
+- 3: backend, {2}, assembler
//生成目標文件
+- 4: assembler, {3}, object
//鏈接:鏈接需要的動態庫和靜態庫,生成可執行文件
+- 5: linker, {4}, image
//通過不同的架構,生成對應的可執行文件
6: bind-arch, "x86_64", {5}, image
1、預處理階段
預處理階段是進行宏的替換和頭文件的導入
,我們可以通過下面命令來查看導入和替換情況
//在終端直接查看替換結果
clang -E main.m
//生成對應的文件查看替換后的源碼
clang -E main.m >> main2.m
-
typedef
給數據類型取別名時,在預處理階段不會被替換
-
define
的取的別名在預處理階段會被替換
,所有我們可以用這方法來進行關鍵代碼混淆,例如將關鍵類和方法用系統類似的名稱取別名
2、編譯階段
編譯階段主要是進行:詞法、語法分析和代碼錯誤檢查
,然后生成中間代碼IR
(1)詞法分析
預處理完成后就會進行詞法分析
,會把代碼切成一個個Token
,比如大小括號,等于號還有字符串等
- 通過下面命令查看詞法分析后結果
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
- 如果頭文件找不到,我們可以通過下面命令指定sdk
clang -isysroot (自己SDK路徑) -fmodules -fsyntax-only -Xclang -dump-tokens main.m
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -fmodules -fsyntax-only -Xclang -dump-tokens main.m
(2)語法分析
驗證語法是否正確
,在詞法分析的基礎上將單詞序列組合成各類語法短語,如程序、語句、表達式等等,然后將所有節點組成抽象語法樹(Abstract Syntax Tree,AST)
。語法分析程序判斷源程序在結構上是否正確
- 我們可以通過下面命令查看語法分析的結果
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
- 如果導入頭文件找不到,可以指定SDK
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.1.sdk/ -fmodules -fsyntax-only -Xclang -ast-dump main.m
下面是幾個關鍵字的含義
- -FunctionDecl 函數
- -ParmVarDecl 參數
- -CallExpr 調用一個函數
- -BinaryOperator 運算符
(3)生成中間代碼IR
代碼生成器(Code Generaltion)
會將語法樹自頂向下遍歷逐步翻譯成LLVM IR
,OC代碼會在這一步進行runtime的橋接:property合成,ARC處理等
- 我們可以通過下面命令生成.ll的文本文件,來查看IR代碼
clang -S -fobjc-arc -emit-llvm main.m
IR基本語法
@ 全局標識
% 局部標識
alloca 開辟空間
align 內存對齊
i32 32bit,4個字節
store 寫入內存
load 讀取數據
call 調用函數
ret 返回
- IR文件在OC中是可以進行優化的,一般設置是在
target - Build Setting - Optimization Level(優化器等級)
中設置。LLVM的優化級別分別是-O0 -O1 -O2 -O3 -Os(第一個是大寫英文字母O)
,下面是帶優化的生成中間代碼IR的命令
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
- xcode7以后開啟
bitcode
,蘋果會做進一步優化,生成.bc的中間代碼,我們通過優化后的IR代碼生成.bc代碼
clang -emit-llvm -c main.ll -o main.bc
3、后端
LLVM在后端主要是會通過一個個的Pass去優化,每個Pass做一些事情,最終生成匯編代碼
- 我們通過最終的
.bc或者.ll代碼
生成匯編代碼
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
- 生成的匯編代碼也可以進行優化
clang -Os -S -fobjc-arc main.m -o main.s
4、生成目標文件
是匯編器以匯編代碼作為插入
,將匯編代碼轉換為機器代碼
,最后輸出目標文件(object file)
clang -fmodules -c main.s -o main.o
- 通過nm命令,查看main.o中的符號
xcrun nm -nm main.o
-
external
表示這個符號是可以外部訪問
的 -
_printf
函數是一個是undefined 、external
的 -
undefined
表示在當前文件暫時找不到符號_printf
5、鏈接
鏈接主要是鏈接需要的動態庫和靜態庫
- 靜態庫和可執行文件合并
- 動態庫是獨立鏈接
鏈接器會吧編譯生成的.o文件
和(.dylib.a)文件
,生成一個mach-o文件
clang main.o -o main
查看鏈接后的符號
xcrun nm -nm main
如下所示,其中undefined
表示會在運行時進行動態綁定
6、綁定
綁定主要是通過不同的架構,生成對應的mach-o格式可執行文件