1. LLVM
1.1 LLVM概述
LLVM是架構編譯器的框架系統,以C++撰寫而成,用于優化任意程式語言撰寫的程式的編譯時間(compile-time)、鏈接時間(link-time)、運行時間(run-time)以及空閑時間(idle-time),對開發者保持開放,并兼容已有腳本,目前LLVM已經被蘋果IOS開發工具,Xilinx Vivado, Facebook,Google等各大公司采用,
1.2 傳統編譯器設計
原始碼 Source Code + 前端 Frontend + 優化器 Optimizer + 后端 Backend(代碼生成器 CodeGenerator)+ 機器碼 Machine Code,如下圖所示

-
前端Frontend:負責決議源代碼,它會進行:詞法分析、語法分析、語意分析,檢查源代碼是否存在錯誤,然后構建針對語言的抽象語法樹(AST:Abstract Syntax Tree,LLVM 的前端還會生成中間代碼(intermediate representation,簡稱IR), -
優化器 Optimizer:優化器負責進行各種優化,改善代碼的運行時間,例如消除冗余計算等; -
后端 Backend(代碼生成器 Code Generator):將代碼映射到目標指令集,生成機器代碼,并且進行機器代碼相關的代碼優化;
1.3 ios的編譯器架構
OC、C、C++使用的編譯器前端是Clang,Swift是swift,后端都是LLVM,如下圖所示

1.4 LLVM的設計
LLVM設計的最重要方面是,使用通用的代碼表示形式(IR),它是用來在編譯器中表示代碼的形式,所有LLVM可以為任何編程語言獨立撰寫前端,并且可以為任意硬體架構獨立撰寫后端,做到了前后端分離如下所示

傳統的編譯器,前端,優化器和后端是連在一起的,是一個專案,但是在llvm中,前端和后端分開了,兩者中間有一個通用的中間層,也就是IR,前端決議源代碼,然后詞法分析、語法分析、語意分析、AST等作業完成之后,生成IR輸出給優化器,優化器負責優化IR代碼,然后后端接受IR代碼后根據需要適配的設備生成X86、ARM64等,所以,當出現一個新設備,只需要研發一個新設備的后端,出現一個高級語言,就研發高級語言的前端,這樣就能支持所有的語言和設備,
1.5 Clang
clang是LLVM專案中的一個子專案,它是基于LLVM架構圖的輕量級編譯器,誕生之初是為了替代GCC,提供更快的編譯速度,它是負責C、C++、OC語言的編譯器,屬于整個LLVM架構中的 編譯器前端,對于開發者來說,研究Clang可以給我們帶來很多好處
2. 編譯流程
可以通過以下命令列印原始碼的編譯階段:
clang -ccc-print-phases main.m
這里新建一個后通過命令列印原始碼的編譯階段:

- 0 -
輸入檔案:找到源檔案 - 1 -
預處理階段:這個程序處理包括宏的替換,頭檔案的匯入 - 2 -
編譯階段:進行詞法分析、語法分析、檢測語法是否正確,最終生成IR - 3 -
后端:這里LLVM會通過一個一個的pass去優化,每個pass做一些事情,最終生成匯編代碼 - 4 -
匯編代碼生成目標檔案 - 5 -
鏈接:鏈接需要的動態庫和靜態庫,生成可執行檔案 - 6 -
系結:通過不同的架構,生成對應架構的可執行檔案
在main.m中輸入一些代碼,

然后通過 指令clang -E main.m >> main1.m生成預處理之后的檔案,
開頭是一些宏展開和.h檔案的展開,

然后最后看到main函式,這里成C沒有了,變成了30.

所以我們得出:
typedef不是預處理指令,也就是說:typedef可以給資料型別取別名,但是在預處理階段不會被替換掉,define則在預處理階段會被替換,所以經常被是用來進行代碼混淆,目的是為了app安全,
3. 編譯階段
編譯階段會進行詞法分析、語法分析、檢測語法是否正確,最終生成IR,
3.1 詞法分析
預處理完成后就會進行詞法分析,這里會把代碼切成一個個Token,比如大小括號,等于號還有字串等, 通過下列指令來查看詞法分析
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
詞法分析結果:

3.2 語法分析
詞法分析完成后就是語法分析,它的任務是驗證語法是否正確,在詞法分析的基礎上將單詞序列組合成各類詞法短語,如程式、陳述句、運算式 等等,然后將所有節點組成抽象語法樹(Abstract Syntax Tree,AST),語法分析程式判斷程式在結構上是否正確,
通過下列指令來查看語法分析
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
得到下面的結果(這里面的地址是虛擬地址,還沒開辟記憶體,可以看作是檔案的偏移地址):

FunctionDecl:函式方法宣告,ParmVarDecl: 引數宣告,CompoundStmt:復合陳述句,CallExpr:函式呼叫,BinaryOperator: 運算子,ImplicitCastExpr:函式指標,DeclRefExpr:函式型別,
3.3 生成中間代碼IR
完成以上步驟后,就開始生成中間代碼IR了,代碼生成器(Code Generation)會將語法樹自頂向下遍歷逐步翻譯成LLVM IR,
簡化一下代碼:

然后通過下列指令來生成 .ll 的文本檔案,查看IR代碼,
clang -S -fobjc-arc -emit-llvm main.m
生成IR代碼如下(這一步會進行語法檢查):

@:全域標識%:區域標識alloca: 開辟空間align: 記憶體對齊i32: 32bit,4個位元組store: 寫入記憶體load: 讀取資料call: 呼叫函式ret: 回傳
上面的IR代碼是沒有經過優化的,所以會比較長, LLVM的優化級別分別是: -O0 , -O1 , -O2 , -O3 , -Os , 可以在xcode里面 target -> Build Settings -> optimization Level 設定優化等級,

輸入下列指令來生成優化后的IR代碼,
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
下面是優化后的IR代碼,可以明顯看出來代碼少了很多,優化等級并不是越高越好的,一般情況下,debug模式下是不進行優化的,而在release模式下是-Os 優化等級,

3.4 bitCode
xcode7以后開啟bitcode,蘋果會做進一步優化,生成.bc的中間代碼,我們通過優化后的IR代碼生成.bc代碼,Bitcode的目的是根據不同的CPU架構,蘋果能夠在APPStore直接下載不同的架構的包,
輸入下列指令來生成bc代碼,
clang -emit-llvm -c main.ll -o main.bc
4. 生成匯編代碼
到了這一步,這里就到了backend,這里LLVM會通過一個一個的pass去優化,每個pass做一些事情,最終生成匯編代碼,
我們通過生成的.bc或者.ll代碼生成匯編代碼,
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
這里分別通過main.ll,main.bc,main.m來生成匯編之后進行對比,

main.bc生成的匯編代碼:

main.ll生成的匯編代碼:

main.m生成的匯編代碼:

這里發現通過main.bc 和 main.ll 生成的匯編代碼都是54行,說明并沒有額外進行代碼優化,main.m是沒有經過優化的原始碼,轉化為匯編后則多了幾行代碼,那么這里的代碼是否還能進行優化呢?試一下, 輸入以下代碼
clang -Os -S -fobjc-arc main.bc -o main3.s
這是指令運行后得到的代碼,發現比之前的又少了幾行,這就說明:當選定了優化等級了之后,在不同的節點上,還能進行優化,

5. 生成目標檔案(匯編器)
目標檔案的生成,是匯編器以匯編代碼作為插入,將匯編代碼轉換為機器代碼,最后輸出目標檔案(object file),
通過以下指令來生產.o檔案
clang -fmodules -c main.s -o main.o
可以通過nm命令,查看下main.o中的符號
$xcrun nm -nm main.o
指令執行后發現輸出下面的結果:

_printf函式是一個是undefined、external的undefined表示在當前檔案暫時找不到符號_printfexternal表示這個符號是外部可以訪問的
這里為什么undefined了呢?因為這里呼叫了外部的方法,這個時候就需要鏈接了,
6. 生成可執行檔案(鏈接)
鏈接主要是鏈接需要的動態庫和靜態庫,生成可執行檔案,
連接器把編譯生成的.o檔案和 .dyld .a檔案鏈接,生成一個mach-o檔案
clang main.o -o main
查看鏈接之后的符號
$xcrun nm -nm main
指令執行后得到下面的結果:

這里看到有兩個undefined,一個是_printf,一個是dyld_stub_binder,但是后面都有(from libSystem),這里的dyld_stub_binder也是一個外部函式,在dyld里面,當mach-o 進入到記憶體之后,外部符號就會和binder進行系結,這個程序是dyld強制系結的,這里就是去系結_printf, 鏈接就是要知道內部的符號是在外面的哪個庫里面,系結就是將外面的函式的地址和內部的符號進行系結,鏈接在編譯期,系結在執行期,所以只要鏈接就一定有一個外部函式也就是dyld_stub_binder,
7. clang 插件
7.1 LLVM下載
由于國內網路限制,需要借助鏡像下載llvm的原始碼鏈接: [link](https://mirror.tuna.tsinghua.edu.cn/help/llvm/).
復制代碼
下載LLVM專案
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git
在LLVM的tool目錄下下載Clang
cd llvm/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git
在LLVM的projects目錄下下載compiler-rt、libcxx、libcxxabi
cd ../projects
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.g it
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git git clone
https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git
在Clang的tools下安裝extra工具
cd ../tools/clang/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-extra.git
7.2 LLVM 編譯
由于最新的LLVM只支持cmake來編譯,所以需要安裝cmake
查看brew是否安裝cmake,如果已經安裝,則跳過下面步驟
brew list
通過brew安裝cmake
brew install cmake
7.3 編譯LLVM
通過xcode編譯LLVM
- cmake編譯成Xcode專案
mkdir build_xcode
cd build_xcode
cmake -G Xcode ../llvm
- 使用xcode編譯Clang
選擇手動創建schemes
編譯(CMD + B),選擇ALL_BUILD Secheme進行編譯,預計1+小時,
通過ninja編譯LLVM
使用ninja進行編譯則還需要安裝ninja,使用以下命令安裝ninja
brew install ninja
在LLVM原始碼根目錄下新建一個build_ninja目錄,最侄訓在build_ninja目錄下生成``build.ninja`
在LLVM原始碼根目錄下新建llvm_release目錄,最終編譯檔案會在llvm_release檔案夾路徑下
cd llvm_build
//注意DCMAKE_INSTALL_PREFIX后面不能有空格
cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX= 安裝路徑(本機為/ Users/xxx/xxx/LLVM/llvm_release)
依次執行編譯,安裝指令
ninja
ninja install
7.4 創建插件
在/llvm/tools/clang/tools下新建插件LSPlugin

在/llvm/tools/clang/tools目錄下的CMakeLists.txt檔案,新增add_clang_subdirectory(LSPlugin),

在LSPlugin目錄下新建 LSPlugin.cpp 和CMakeLists.txt,并在CMakeLists.txt中加上以下代碼
add_llvm_library( HKPlugin MODULE BUILDTREE_ONLY
LSPlugin.cpp
)

接下來利用cmake重新生成Xcode專案,在build_xcode目錄下執行以下命令
cmake -G Xcode ../llvm
最后可以在LLVM的xcode專案中可以看到Loadable modules目錄下由自定義的LSPlugin目錄了,然后可以在里面撰寫插件代碼了,
面試基礎
iOS面試基礎知識 (一)
iOS面試基礎知識 (二)
iOS面試基礎知識 (三)
iOS面試基礎知識 (四)
iOS面試基礎知識 (五)
知識詳解
iOS面試要點之GCD面試要點
iOS面試要點之多執行緒面試要點
iOS面試要點之block面試要點
iOS面試要點之Runtime面試要點
iOS面試要點之RunLoop面試要點
iOS面試要點之記憶體管理面試要點
iOS面試要點之MVC、MVVM面試要點
iOS面試要點之網路性能優化要點
iOS面試要點之網路編程面試要點
iOS面試要點之KVC&KVO面試要點
iOS面試要點之資料存盤面試要點
iOS面試要點之混編技術面試要點
iOS面試要點之設計模式面試要點
iOS面試要點之UI面試要點
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/296463.html
標籤:其他
下一篇:華為動態標簽管理如何助力廣告營銷
