目錄
一、LLVM概述
LLVM是構(gòu)架編譯器(compiler
)的框架系統(tǒng),以C+ +
編寫而成,用于優(yōu)化以任意程序語言編寫的程序的編譯時(shí)間(compile- time
)、鏈接時(shí)間(ink-time
)、 運(yùn)行時(shí)間(run-time
)以及空閑時(shí)間(idle-time
),對(duì)開發(fā)者保持開放,并兼容已有腳本。LLVM
計(jì)劃啟動(dòng)于2000年,最初由美國UIUC大學(xué)
的Chris Lattner博士
主持開展。2006年Chris Lattner
加盟Apple Inc.
并致力于LLVM
在Apple
開發(fā)體系中的應(yīng)用。
Apple
也是LLVM
計(jì)劃的主要資助者。目前LLVM
已經(jīng)被蘋果IOS開發(fā)工具、Xilinx Vivado、Facebook、Google
等各大公司采用。
傳統(tǒng)編譯器設(shè)計(jì)
編譯器前端(Frontend)
編譯器前端的任務(wù)是解析源代碼
。它會(huì)進(jìn)行:詞法分析,語法分析,語義分析,檢查源代碼是否存在錯(cuò)誤
,然后構(gòu)建抽象語法樹(Abstract Syntax Tree,AST)
,LLVM
的前端還會(huì)生成中間代碼(intermediate representation, IR)
。
優(yōu)化器(Optimizer)
優(yōu)化器負(fù)責(zé)進(jìn)行各種優(yōu)化,改善代碼的運(yùn)行時(shí)間,例如消除冗余計(jì)算等。優(yōu)化器接收和輸出均是IR。
后端(Backend) /代碼生成器(CodeGenerator)
將代碼映射到目標(biāo)指令集
。生成機(jī)器語言,并且進(jìn)行機(jī)器相關(guān)的代碼優(yōu)化。
iOS的編譯器架構(gòu)
Objective C/C/C++
使用的編譯器前端是Clang
, Swift
是Swift
,后端都是LLVM。
LLVM的設(shè)計(jì)
當(dāng)編譯器決定支持多種源語言或多種硬件架構(gòu)時(shí),LLVM
最重要的地方就來了。其他的編譯器如GCC
,它方法非常成功,但由于它是作為整體應(yīng)用程序設(shè)計(jì)的,因此它們的用途受到了很大的限制。
LLVM
設(shè)計(jì)的最重要方面是,使用通用的代碼表示形式(IR
) ,它是用來在編譯器中表示代碼的形式。所以LLVM
可以為任何編程語言獨(dú)立編寫前端,并且可以為任意硬件架構(gòu)獨(dú)立編寫后端。
Clang
Clang是LLVM
項(xiàng)目中的一個(gè)子項(xiàng)目。它是基于LLVM
架構(gòu)的輕量級(jí)編譯器,誕生之初是為了替代GCC
,提供更快的編譯速度。它是負(fù)責(zé)編譯C、C++、 Objecte-C
語言的編譯器,它屬于整個(gè)LLVM
架構(gòu)中的,編譯器前端。對(duì)于開發(fā)者來說,研究Clang
可以給我們帶來很多好處。
二、編譯流程
0、通過命令打印源碼編譯的各個(gè)階段
clang -ccc-print-phases main.m
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
0:輸入文件:找到源文件。
1:預(yù)處理階段:這個(gè)過程處理包括宏的替換,頭文件的導(dǎo)入。
2:編譯階段:進(jìn)行詞法分析、語法分析、檢測(cè)語法是否正確,最終生成IR。
3:后端:這里L(fēng)LVM會(huì)通過一個(gè)一個(gè)的Pass去優(yōu)化,每個(gè)Pass做一些事情,最終生成匯編代碼。
4:生成目標(biāo)文件。
5:鏈接:鏈接需要的動(dòng)態(tài)庫和靜態(tài)庫,生成可執(zhí)行文件。
6:通過不同的架構(gòu),生成對(duì)應(yīng)的可執(zhí)行文件。
1、預(yù)處理階段
1.1、define
main.m中的代碼如下:
#import <stdio.h>
#define C 30
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
int b = 10;
printf("%d",a + b + C);
}
return 0;
}
執(zhí)行如下命令
clang -E main.m >> main2.m
執(zhí)行完畢可以看到頭文件的導(dǎo)入和宏的替換
1.2、typedef
#import <stdio.h>
#define C 30
typedef int CJ_INT_64;
int main(int argc, const char * argv[]) {
@autoreleasepool {
CJ_INT_64 a = 10;
CJ_INT_64 b = 10;
printf("%d",a + b + C);
}
return 0;
}
預(yù)處理
typedef int CJ_INT_64;
int main(int argc, const char * argv[]) {
@autoreleasepool {
CJ_INT_64 a = 10;
CJ_INT_64 b = 10;
printf("%d",a + b + 30);
}
return 0;
}
define
用于給數(shù)據(jù)
取別名(宏
),預(yù)處理階段會(huì)被替換;typedef
用于給類型
取別名,預(yù)處理階段不會(huì)被替換。
define
使用例子:由于會(huì)在預(yù)處理階段被替換,因此可用來給敏感的數(shù)據(jù)、方法、類名取別名,代碼混淆,方法加鹽等。eg:#define isVIP isxxxVxxxIxxxP
2、編譯階段
2.1、詞法分析
預(yù)處理完成后就會(huì)進(jìn)行詞法分析。這里會(huì)把代碼切成一個(gè)個(gè)Token,比如大小括號(hào),等于號(hào)還有字符串等。
clang - fmodules -fsyntax-only -Xclang -dump-tokens main.m
2.2、語法分析
詞法分析完成之后就是語法分析,它的任務(wù)是驗(yàn)證語法是否正確。在詞法分析的基礎(chǔ)上將單詞序列組合成各類語法短語,如“程序”, “語句”, “表達(dá)式”
等等,然后將所有節(jié)點(diǎn)組成抽象語法樹(Abstract Syntax Tree, AST)
。 語法分析程序判斷源程序在結(jié)構(gòu)上是否正確。
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
如果導(dǎo)入頭文件找不到(比如導(dǎo)入Foundation.h或者UIKit.h),那么可以指定SDK
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk(自己S
DK路徑) -fmodules -fsyntax-only -Xclang -ast-dump main.m
2.3、生成中間代碼IR(intermediate representation )
完成以上步驟后就開始生成中間代碼IR
了,代碼生成器(Code Generation)
會(huì)
將語法樹自頂向下遍歷逐步翻譯成LLVM IR
。通過下面命令可以生成.ll
的文本文
件,查看IR
代碼。
clang -S -fobjc-arc -emit-llvm main.m
Objective C代碼在這一步 會(huì)進(jìn)行runtime
的橋接: property
合成, ARC
處理等
IR的基本語法
@全局標(biāo)識(shí)
%局部標(biāo)識(shí)
alloca開辟空間
align內(nèi)存對(duì)齊
i32 32個(gè)bit, 4個(gè)字節(jié)
store寫入內(nèi)存
load讀取數(shù)據(jù)
call調(diào)用函數(shù)
ret返回
IR的優(yōu)化
LLVM
的優(yōu)化級(jí)別分別是-O0 -O1 -O2 -O3 -Os(第一個(gè)是大寫英文字母O)
clang -Os -S - fobjc-arc -emit-llvm main.m -o main.ll
bitCode
xcode7
以后開啟bitcode
蘋果會(huì)做進(jìn)一步的優(yōu)化。 生成.bc
的中間代碼。
我們通過優(yōu)化后的IR
代碼生成.bc
代碼
clang -emit-llvm -c main.ll -o main.bc
3、生成匯編代碼
我們通過最終的.b
c或者.ll
代碼生成匯編代碼
clang -S -fobjc-arc main.bc -o main.s
clang -S - fobjc-arc main.ll -o main.s
生成匯編代碼也可以進(jìn)行優(yōu)化
clang -Os -S -fobjc-arc main.m -o main.s
4、生成目標(biāo)文件(匯編器)
目標(biāo)文件的生成,是匯編器以匯編代碼作為輸入,將匯編代碼轉(zhuǎn)換為機(jī)器代碼,最后輸出目標(biāo)文件(object file
)。
clang -fmodules -c main.s -o main.o
通過nm
命令,查看下main.o
中的符號(hào)
$xcrun nm -nm main. 0
(undefined) external_ _printf
000000000000000 (__ TEXT,_ text) external_test
00000000000000a (__ TEXT,_ text) external_main
_printf
是一個(gè)是undefined external
的。
undefined
表示在當(dāng)前文件暫時(shí)找不到符號(hào)_printf
。
external
表示這個(gè)符號(hào)是外部可以訪問的。
5、生成可執(zhí)行文件(鏈接)
鏈接器把編譯產(chǎn)生的.o
文件和(.dylib.a)
文件,生成一個(gè)mach-o
文件。
clang main.o -o main
查看鏈接之后的符號(hào)
$xcrun nm -nm main
(undefined) external_ printf ( from libSystem)
(undefined) external dyld_ stub_ _binder (from libSystem)
0000000000000 (_ TEXT,_ text) [referenced dynamically] external_mh_execute_header
000000100000f6d (__ TEXT,_ text) external_ test
000000100000f77 (__ TEXT,_ text) external_ main