LLVM概述
LLVM是架構(gòu)編譯器的框架系統(tǒng),以C++編寫而成,用于優(yōu)化任意程序語(yǔ)言編寫的程序的編譯時(shí)間(compile-time)、鏈接時(shí)間(link-time)、運(yùn)行時(shí)間(run-time)以及空閑時(shí)間(idle-time)。
LLVM的設(shè)計(jì)
LLVM的一大特色就是,有著獨(dú)立的、完善的、嚴(yán)格約束的中間代碼表示。這種中間代碼,就是LLVM的字節(jié)碼,是LLVM抽象的精髓,前端生成這種中間代碼,后端自動(dòng)進(jìn)行各類優(yōu)化分析,讓用LLVM開發(fā)的編譯器,都能用上最先見(jiàn)的后端優(yōu)化技術(shù)。

-
Frontend
????編譯器前端的任務(wù)是解析源代碼(編譯階段),它會(huì)進(jìn)行 詞法分析、語(yǔ)法分析、語(yǔ)義分析、檢查源代碼是否存在錯(cuò)誤,然后構(gòu)建抽象語(yǔ)法樹(Abstract Syntax Tree AST),LLVM的前端還會(huì)生成中間代碼(intermediate representation,簡(jiǎn)稱IR),可以理解為llvm是編譯器 + 優(yōu)化器, 接收的是IR中間代碼,輸出的還是IR,給后端,經(jīng)過(guò)后端翻譯成目標(biāo)指令集
-
優(yōu)化器 Optimizer
????優(yōu)化器負(fù)責(zé)進(jìn)行各種優(yōu)化,改善代碼的運(yùn)行時(shí)間,例如消除冗余計(jì)算等
-
后端 Backend(代碼生成器 Code Generator)
????將代碼映射到目標(biāo)指令集,生成機(jī)器代碼,并且進(jìn)行機(jī)器代碼相關(guān)的代碼優(yōu)化。
LLVM將中間代碼優(yōu)化這個(gè)流程做到了極致,LLVM工具鏈,不但可以生成所支持的各個(gè)后端平臺(tái)的代碼,更可以方便的將各語(yǔ)言的前端編譯后的模塊鏈接到一起,你可以方便的在你的語(yǔ)言中調(diào)用C函數(shù)。

iOS的編譯器架構(gòu)
OC 、C、C++ 使用的編譯器前端是 Clang ,后端都是LLVM,如下圖所示

Clang概述
Clang 是一個(gè) C++ 編寫、基于 LLVM、發(fā)布于 LLVM BSD 許可證下的 C/C++/Objective C/Objective C++ 編譯器。
Clang 編譯流程
通過(guò)命令可以打印源碼的編譯階段
clang -ccc-print-phases 源文件路徑
得到結(jié)果如下:
輸入文件:找到源文件
input, "main.m", objective-c預(yù)處理階段:這個(gè)過(guò)程處理包括宏的替換,頭文件的導(dǎo)入
preprocessor, {0}, objective-c-cpp-output編譯階段:進(jìn)行詞法分析、語(yǔ)法分析、檢測(cè)語(yǔ)法是否正確,最終生成IR
compiler, {1}, ir后端:這里L(fēng)LVM會(huì)通過(guò)一個(gè)一個(gè)的pass去優(yōu)化,每個(gè)pass做一些事情,最終生成匯編代碼
backend, {2}, assembler匯編代碼生成目標(biāo)文件
assembler, {3}, object鏈接:鏈接需要的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù),生成可執(zhí)行文件
linker, {4}, image(鏡像文件)綁定:通過(guò)不同的架構(gòu),生成對(duì)應(yīng)的可執(zhí)行文件
bind-arch, "x86_64", {5}, image
預(yù)處理階段
????這個(gè)階段主要是處理包括宏的替換,頭文件的導(dǎo)入,可以執(zhí)行命令 clang -E 源文件路徑,執(zhí)行完畢可以看到頭文件的導(dǎo)入和宏的替換。define則在預(yù)處理階段會(huì)被替換,所以經(jīng)常被是用來(lái)進(jìn)行代碼混淆,目的是為了 app 安全,實(shí)現(xiàn)邏輯是:將 app 中核心類、核心方法等用系統(tǒng)相似的名稱進(jìn)行取別名,然后在預(yù)處理階段就被替換,來(lái)達(dá)到代碼混淆的目的。
clang -E main.m
編譯階段
詞法分析
預(yù)處理完成后就會(huì)進(jìn)行詞法分析,這里會(huì)把代碼切成一個(gè)個(gè) token,比如大小括號(hào)、等于號(hào)、還有字符串等,
clang -fmodules -fsyntax-only -Xclang -dump-tokens 源文件路徑
語(yǔ)法分析
語(yǔ)法分析,它的任務(wù)是驗(yàn)證語(yǔ)法是否正確,在詞法分析的基礎(chǔ)上將單詞序列組合成各類此法短語(yǔ),如程序、語(yǔ)句、表達(dá)式 等等,然后將所有節(jié)點(diǎn)組成抽象語(yǔ)法樹(Abstract Syntax Tree AST),語(yǔ)法分析程序判斷程序在結(jié)構(gòu)上是否正確。
clang -fmodules -fsyntax-only -Xclang -ast-dump 源文件路徑
-
FunctionDecl函數(shù) -
ParmVarDecl參數(shù) -
CallExpr調(diào)用一個(gè)函數(shù) -
BinaryOperator運(yùn)算符
生成中間代碼IR
完成以上步驟后,就開始生成中間代碼 IR 了,代碼生成器(Code Generation)會(huì)將語(yǔ)法樹自頂向下遍歷逐步翻譯成 LLVM IR,可以通過(guò)下面命令可以生成 .ll 的文本文件,查看 IR 代碼。
clang -S -fobjc-arc -emit-llvm 源文件路徑 -o 輸出文件路徑
Objective-C 代碼在這一步會(huì)進(jìn)行 runtime 橋接,property 合成、ARC 處理等。
IR 基本語(yǔ)法
-
@全局標(biāo)識(shí) -
%局部標(biāo)識(shí) -
alloca開辟空間 -
align內(nèi)存對(duì)齊 -
i3232bit,4個(gè)字節(jié) -
store寫入內(nèi)存 -
load讀取數(shù)據(jù) -
call調(diào)用函數(shù) -
ret返回
Optimization Level
當(dāng)然,IR 文件在 Objective-C 中是可以進(jìn)行優(yōu)化的,一般設(shè)置是在 target - Build Setting - Optimization Level(優(yōu)化器等級(jí))中設(shè)置。

LLVM 的優(yōu)化級(jí)別分別是
-O0、 -O1 -O2 -O3 -Os,下面是帶優(yōu)化的生成中間代碼 IR 的命令
clang -Os -S -fobjc-arc -emit-llvm 源文件路徑 -o 輸出文件路徑
BitCode
xcode7 以后開啟 bitcode ,蘋果會(huì)做進(jìn)一步優(yōu)化,生成 .bc 的中間代碼,我們通過(guò)優(yōu)化后的 IR 代碼生成 .bc代碼
clang -emit-llvm -c .ll源文件 -o .bc文件
后端
LLVM 在后端主要是會(huì)通過(guò)一個(gè)個(gè)的 Pass 去優(yōu)化,每個(gè) Pass 做一些事情,最終生成匯編代碼。我們通過(guò)最終的 .bc 或者 .ll 代碼生成匯編代碼:
clang -S -fobjc-arc .bc 文件 -o .s 文件
clang -S -fobjc-arc .ll 文件 -o .s 文件
生成匯編代碼也可以進(jìn)行優(yōu)化
clang -Os -S -fobjc-arc .m 文件 -o .s 文件
生成目標(biāo)文件
目標(biāo)文件的生成,是匯編器以匯編代碼作為插入,將匯編代碼轉(zhuǎn)換為機(jī)器代碼,最后輸出目標(biāo)文件(object file)
clang -fmodules -c .s 文件 -o .o 文件
可以通過(guò) nm 命令,查看下 main.o 中的符號(hào)
xcrun nm -nm .o 文件
undefined表示在當(dāng)前文件暫時(shí)找不到符號(hào)external表示這個(gè)符號(hào)是外部可以訪問(wèn)的
鏈接
鏈接主要是鏈接需要的動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù),生成可執(zhí)行文件,其中靜態(tài)庫(kù)會(huì)和可執(zhí)行文件合并,動(dòng)態(tài)庫(kù)是獨(dú)立的。連接器把編譯生成的 .o 文件和 .dyld、.a 文件鏈接,生成一個(gè) mach-o 文件
clang .o 文件 -o 輸出文件
LLVM 工程編譯
下載源碼
git clone https://github.com/llvm/llvm-project.git
安裝 cmake
brew install cmake
通過(guò) xcode 編譯 LLVM
-
cmake編譯成 Xcode 項(xiàng)目cd llvm-project mkdir build_xcode cd build_xcode cmake -G Xcode ../llvm 打開
build_xcode/LLVM.xcodeproj文件選擇
Automatically Create Schemes(目前 xcode version 13.2.1)選擇
ALL_BUILD Secheme進(jìn)行編譯
通過(guò) ninja 編譯 LLVM
-
安裝
ninjabrew install ninja -
在 llvm-project 目錄下創(chuàng)建文件夾 build_ninja 、 llvm-release
cd llvm-project mkdir build_ninja mkdir llvm_release -
cmake編譯cd build_ninja cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=../llvm_release -
ninja運(yùn)行ninja install