瞻仰大佬
- Clang
- LLVM
- Swift
2010年開始編寫 Swift語言,並且一我的實現了Swift的大部分基礎架構;他也是 LVVM 以及 Clang的主要開發者。
html
LLVM官網前端
做用:用於優化以任意程序語言編寫的程序的編譯時間(compile-time)、連接時間(link-time)、運行時間(run-time)以及空閒時間(idle-time).在2000年,Chris Lattner開發了這一套編譯器工具庫套件.後來隨着LLVM的發展,LLVM能夠用於常規編譯器,JIT編譯器,彙編器,調試器,靜態分析工具等一系列跟編程語言相關的工做。ios
2012年,LLVM 得到美國計算機學會 ACM 的軟件系統大獎,和 UNIX,WWW,TCP/IP,Apache,JAVA, Eclipse等齊名。c++
注:LLVM工程包含了一組模塊化,可複用的編輯器和工具鏈。同其名字原意(Low Level Virtual Machine)不一樣的是,LLVM不是一個首字母縮寫,而是工程的名字。
git
Xcode版本 | 編譯器版本 |
---|---|
Xcode3以前 | GCC |
Xcode3 | GCC與 LLVM混合編譯器 |
Xcode4 | LLVM-GCC 成爲默認編譯器 |
Xcode4.2 | LLVM3.0成爲默認編譯器 |
Xcode5 | LLVM5.0, 完成 GCC到LLVM的過渡 |
GCC是 Xcode早期使用的一個強大的編譯器.這個編譯器被移植到各類系統中,其中就是 Mac OSX 操做系統,因此這就反映在 Xcode中,在早期的 Xcode 調試代碼的一個工具就是 GDB,它是GNU調試器.github
Apple(包括中後期的NeXT)一直使用GCC做爲官方的編譯器。GCC做爲開源世界的編譯器標準一直作得不錯,但Apple對編譯工具會提出更高的要求。 一方面,是Apple對Objective-C語言(甚至後來對C語言)新增不少特性,但GCC開發者並不買Apple的賬——不給實現,所以索性後來二者分紅兩條分支分別開發,這也形成Apple的編譯器版本遠落後於GCC的官方版本。另外一方面,GCC的代碼耦合度過高,很差獨立,並且越是後期的版本,代碼質量越差,但Apple想作的不少功能(好比更好的IDE支持)須要模塊化的方式來調用GCC,但GCC一直不給作。甚至最近,《GCC運行環境豁免條款(英文版)》從根本上限制了LLVM-GCC的開發。 因此,這種不和讓Apple一直在尋找一個高效的、模塊化的、協議更放鬆的開源替代品.
objective-c
- LLVM Core:包含一個如今的源代碼/目標設備無關的優化器,一集一個針對不少主流(甚至於一些非主流)的CPU的彙編代碼生成支持。
- Clang:一個C/C++/Objective-C編譯器,致力於提供使人驚訝的快速編譯,極其有用的錯誤和警告信息,提供一個可用於構建很棒的源代碼級別的工具.
- dragonegg: gcc插件,可將GCC的優化和代碼生成器替換爲LLVM的相應工具。
- LLDB:基於LLVM提供的庫和Clang構建的優秀的本地調試器。
- libc++、libc++ ABI: 符合標準的,高性能的C++標準庫實現,以及對C++11的完整支持。
- compiler-rt:針對__fixunsdfdi和其餘目標機器上沒有一個核心IR(intermediate representation)對應的短原生指令序列時,提供高度調優過的底層代碼生成支持。
- OpenMP: Clang中對多平臺並行編程的runtime支持。
- vmkit:基於LLVM的Java和.NET虛擬機實
- polly: 支持高級別的循環和數據本地化優化支持的LLVM框架。
- libclc: OpenCL標準庫的實現
- klee: 基於LLVM編譯基礎設施的符號化虛擬機
- SAFECode:內存安全的C/C++編譯器
- lld: clang/llvm內置的連接器
傳統的靜態編譯器分爲三個階段:前端、優化和後端。 算法
典型例子:GCC編譯器, 如何作到解耦? 編程
不一樣的前端後端使用統一的中間代碼LLVM Intermediate Representation (LLVM IR)
若是須要支持一種新的編程語言,那麼只須要實現一個新的前端
若是須要支持一種新的硬件設備,那麼只須要實現一個新的後端
優化階段是一個通用的階段,它針對的是統一的LLVM IR,不管是支持新的編程語言,仍是支持新的硬件設備,都不須要對優化階段作修改
LLVM如今被做爲實現各類靜態和運行時編譯語言的通用基礎結構(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)ubuntu
Frontend:前端
Optimizer:優化器
Backend:後端
做爲LLVM提供的編譯器前端,clang可將用戶的源代碼(C/C++/Objective-C)編譯成語言/目標設備無關的IR(Intermediate Representation)實現。其可提供良好的插件支持,允許用戶在編譯時,運行額外的自定義動做。
在列出完整步驟以前能夠先看個簡單例子。看看是如何完成一次編譯的。
#import <Foundation/Foundation.h>
#define DEFINEEight 8
int main(){
@autoreleasepool {
int eight = DEFINEEight;
int six = 6;
NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
int rank = eight + six;
NSLog(@"%@ rank %d", site, rank);
}
return 0;
}
複製代碼
$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m -o main-arm64.cpp
複製代碼
生成的c++文件以下
int main(){
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int eight = 8;
int six = 6;
NSString* site = ((NSString * _Nullable (*)(id, SEL, const char * _Nonnull))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("alloc")), sel_registerName("initWithUTF8String:"), (const char *)"starming");
int rank = eight + six;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_c__8jb7vhc96p1bhvf5gl7zw_9sj925cz_T_main_9c278d_mi_0, site, rank);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
複製代碼
$ clang -rewrite-objc mian.m
複製代碼
$ clang -ccc-print-phases main.m
複製代碼
0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output // 預處理
2: compiler, {1}, ir // 編譯生成IR(中間代碼)
3: backend, {2}, assembler // 彙編器生成彙編代碼
4: assembler, {3}, object // 生成機器碼(目標文件)
5: linker, {4}, image // 連接
6: bind-arch, "x86_64", {5}, image // 根據運行平臺,生成鏡像文件(Image),也就是最後的可執行文件
複製代碼
想看清clang前端的所有過程?接下來能夠繼續經過clang命令查看各階段都作了哪些處理。
$ clang -E main.m
複製代碼
/*
... 頭文件
# 1 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3
# 185 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3
# 2 "main.m" 2
int main(){
@autoreleasepool {
int eight = 8;
int six = 6;
NSString* site = [[NSString alloc] initWithUTF8String:"starming"];
int rank = eight + six;
NSLog(@"%@ rank %d", site, rank);
}
return 0;
}
複製代碼
這個過程的處理包括宏的替換,頭文件的導入,以及相似#if的處理。
預處理完成後就會進行詞法分析,這裏會把代碼切成一個個 Token,好比大小括號,等於號還有字符串等。
$ clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
複製代碼
以下例子:
int testAST(int a, int b) {
while (b != 0) {
if (a > b) {
a = a - b;
} else {
b = b - a;
}
}
return a;
}
複製代碼
驗證語法是否正確,而後將全部節點組成抽象語法樹 AST 。
$ clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
複製代碼
生成以下語法樹:
`-FunctionDecl 0x7ffd8f84d678 <main.m:3:1, line:13:1> line:3:5 test 'int (int, int)'
|-ParmVarDecl 0x7ffd8f84d4f8 <col:10, col:14> col:14 used a 'int'
|-ParmVarDecl 0x7ffd8f84d570 <col:17, col:21> col:21 used b 'int'
`-CompoundStmt 0x7ffd8f84dba8 <col:24, line:13:1>
|-WhileStmt 0x7ffd8f84db30 <line:4:5, line:11:5>
| |-<<<NULL>>>
| |-BinaryOperator 0x7ffd8f84d7d8 <line:4:12, col:17> 'int' '!='
| | |-ImplicitCastExpr 0x7ffd8f84d7c0 <col:12> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x7ffd8f84d778 <col:12> 'int' lvalue ParmVar 0x7ffd8f84d570 'b' 'int'
| | `-IntegerLiteral 0x7ffd8f84d7a0 <col:17> 'int' 0
| `-CompoundStmt 0x7ffd8f84db10 <col:20, line:11:5>
| `-IfStmt 0x7ffd8f84dad8 <line:6:9, line:10:9>
| |-<<<NULL>>>
| |-<<<NULL>>>
| |-BinaryOperator 0x7ffd8f84d880 <line:6:13, col:17> 'int' '>'
| | |-ImplicitCastExpr 0x7ffd8f84d850 <col:13> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x7ffd8f84d800 <col:13> 'int' lvalue ParmVar 0x7ffd8f84d4f8 'a' 'int'
| | `-ImplicitCastExpr 0x7ffd8f84d868 <col:17> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x7ffd8f84d828 <col:17> 'int' lvalue ParmVar 0x7ffd8f84d570 'b' 'int'
| |-CompoundStmt 0x7ffd8f84d9a0 <col:20, line:8:9>
| | `-BinaryOperator 0x7ffd8f84d978 <line:7:13, col:21> 'int' '='
| | |-DeclRefExpr 0x7ffd8f84d8a8 <col:13> 'int' lvalue ParmVar 0x7ffd8f84d4f8 'a' 'int'
| | `-BinaryOperator 0x7ffd8f84d950 <col:17, col:21> 'int' '-'
| | |-ImplicitCastExpr 0x7ffd8f84d920 <col:17> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0x7ffd8f84d8d0 <col:17> 'int' lvalue ParmVar 0x7ffd8f84d4f8 'a' 'int'
| | `-ImplicitCastExpr 0x7ffd8f84d938 <col:21> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x7ffd8f84d8f8 <col:21> 'int' lvalue ParmVar 0x7ffd8f84d570 'b' 'int'
| `-CompoundStmt 0x7ffd8f84dab8 <line:8:16, line:10:9>
| `-BinaryOperator 0x7ffd8f84da90 <line:9:13, col:21> 'int' '='
| |-DeclRefExpr 0x7ffd8f84d9c0 <col:13> 'int' lvalue ParmVar 0x7ffd8f84d570 'b' 'int'
| `-BinaryOperator 0x7ffd8f84da68 <col:17, col:21> 'int' '-'
| |-ImplicitCastExpr 0x7ffd8f84da38 <col:17> 'int' <LValueToRValue>
| | `-DeclRefExpr 0x7ffd8f84d9e8 <col:17> 'int' lvalue ParmVar 0x7ffd8f84d570 'b' 'int'
| `-ImplicitCastExpr 0x7ffd8f84da50 <col:21> 'int' <LValueToRValue>
| `-DeclRefExpr 0x7ffd8f84da10 <col:21> 'int' lvalue ParmVar 0x7ffd8f84d4f8 'a' 'int'
`-ReturnStmt 0x7ffd8f84db90 <line:12:5, col:12>
`-ImplicitCastExpr 0x7ffd8f84db78 <col:12> 'int' <LValueToRValue>
`-DeclRefExpr 0x7ffd8f84db50 <col:12> 'int' lvalue ParmVar 0x7ffd8f84d4f8 'a' 'int'
複製代碼
$ clang -S -fobjc-arc -emit-llvm main.m -o main.ll
複製代碼
到這一步,LLVM前段編譯器clang的工做已經基本作完了。
$ clang -S -emit-llvm main.m -o main.ll
複製代碼
$ clang -c -emit-llvm main.m -o main.bc
複製代碼
In compiler design, static single assignment form (often abbreviated as SSA form or simply SSA) is a property of an intermediate representation (IR), which requires that each variable is assigned exactly once, and every variable is defined before it is used.
– From Wikipedia
從上面的描述能夠看出,SSA 形式的 IR 主要特徵是每一個變量只賦值一次。相比而言,非SSA形式的IR裏一個變量能夠賦值屢次。
能夠簡化不少編譯優化方法的過程;
對不少編譯優化方法來講,能夠得到更好的優化結果,
下面給出一個例子:
int main() {
int x, y;
x = 1;
x = 2;
y = x;
}
複製代碼
y := 1
y := 2
x := y
複製代碼
顯然,咱們一眼就能夠看出,上述代碼第一行的賦值行爲是多餘的,第三行使用的 y 值來自於第二行中的賦值。對於採用非 SSA 形式 IR 的編譯器來講,它須要作數據流分析(具體來講是到達-定義分析)來肯定選取哪一行的 y 值。可是對於 SSA 形式來講,就不存在這個問題了。以下所示:
y1 := 1
y2 := 2
x1 := y2
複製代碼
咱們不須要作數據流分析就能夠知道第三行中使用的y來自於第二行的定義,這個例子很好地說明了SSA的優點。除此以外,還有許多其餘的優化算法在採用SSA形式以後優化效果獲得了極大提升。甚至,有部分優化算法只能在SSA上作。
優化IR:(級別-03)
clang -O3 -S -fobjc-arc -emit-llvm main.m -o main.ll
複製代碼
若是開啓了 bitcode 蘋果會作進一步的優化,有新的後端架構仍是能夠用這份優化過的 bitcode 去生成。
生成字節碼:
clang -emit-llvm -c main.m -o main.bc
複製代碼
注:xx.bc文件是位流格式,因爲是二進制的,因此直接看就是一堆亂碼,查看bitcode最好的方式是用hexdump工具。
clang -S -fobjc-arc main.m -o main.s
複製代碼
clang -fmodules -c main.m -o main.o
複製代碼
clang main.o -o main
執行
./main
輸出
starming rank 14
複製代碼
1.優先編譯cocopods裏面的全部依賴文件
個人深圳編譯流程:
brew是一個軟件包管理工具,相似於centos下的yum或者ubuntu下的apt-get,很是方便,免去了本身手動編譯安裝的不便
brew 安裝目錄 /usr/local/Cellar
brew 配置目錄 /usr/local/etc
brew 命令目錄 /usr/local/bin
注:homebrew在安裝完成後自動在/usr/local/bin加個軟鏈接,因此日常都是用這個路徑
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
複製代碼
CMake是一個跨平臺的編譯(Build)工具,能夠用簡單的語句來描述全部平臺的編譯過程。
$ brew install cmake
複製代碼
Ninja 是一個構建系統,與 Make 相似。做爲輸入,你須要描述將源文件處理爲目標文件這一過程所需的命令。 Ninja 使用這些命令保持目標處於最新狀態。 Ninja 的主要設計目標是速度。
$ brew install ninja
複製代碼
// 大小648.2M
$ git clone https://git.llvm.org/git/llvm.git/
複製代碼
// 大小240.6M
$ cd llvm/tools
$ git clone https://git.llvm.org/git/clang.git/
複製代碼
$ cd llvm_build
$ cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=LLVM的安裝路徑
複製代碼
$ ninja
複製代碼
編譯完畢後, 【llvm_build】目錄大概 21.05 G(僅供參考)
$ ninja install
複製代碼
安裝完畢後,安裝目錄大概 11.92 G(僅供參考)
$ cd llvm_xcode
$ cmake -G Xcode ../llvm
複製代碼
在【llvm/tools/clang/tools】源碼目錄下新建一個插件目錄,假設叫作【yb-plugin】
在【llvm/tools/clang/tools/CMakeLists.txt】最後加入內容: add_clang_subdirectory(yb-plugin),小括號裏是插件目錄名
# libclang may require clang-tidy in clang-tools-extra.
add_clang_subdirectory(libclang)
add_clang_subdirectory(yb-plugin)
複製代碼
add_llvm_loadable_module(YBPlugin YBPlugin.cpp)
複製代碼
MJPlugin是插件名,MJPlugin.cpp是源代碼文件
生成xcode項目
編寫插件
編譯插件生成動態庫文件
-Xclang -load -Xclang 動態庫路徑 -Xclang -add-plugin -Xclang 插件名稱
複製代碼
下載【XcodeHacking.zip】,解壓,修改【HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec】的內容,設 置一下本身編譯好的clang的路徑
![]()
$ sudo mv HackedClang.xcplugin `xcode-select-print- path`/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins
$ sudo mv HackedBuildSystem.xcspec `xcode-select-print- path`/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications
複製代碼
代碼混淆
APP包瘦身
開發新的編程語言