本文爲筆記型式呈現,並不是所有原創,來源見文末html
Apple(包括中後期的NeXT) 一直使用GCC做爲官方的編譯器。GCC做爲開源世界的編譯器標準一直作得不錯,但Apple對編譯工具會提出更高的要求。前端
Clang這個軟體專案在2005年由蘋果電腦發起,是LLVM編譯器工具集的前端(front-end),目的是輸出程式碼對應的抽象語法樹(Abstract Syntax Tree, AST),並將程式碼編譯成LLVM Bitcode。接着在後端(back-end)使用LLVM編譯成平臺相關的機器語言 。java
main.mgit
#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;
}
複製代碼
直接編譯成執行檔github
clang -fmodules main.m
objective-c
產出 .out (executable)後端
是一個C、C++、Objective-C和Objective-C++程式語言的編譯器前端api
clang -ccc-print-phases main.m
session
指定語言 , 架構, 輸入file架構
clang -x objective-c main.m
import 頭文件, include頭文件等 ,macro宏展開,處理'#'開頭指令
單作預處理, 並取得預處理結果
clang -E main.m
預處理最終結果:
# 1 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3
# 185 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.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;
}
複製代碼
依據上面的結果能明顯看到 header被換成了明確的全局位置,常量DEFINEEight
也被替換進代碼裏,講到import 就不得不提 modules
文章裏說起: Modules provide an alternative, simpler way to use software libraries that provides better compile-time scalability and eliminates many of the problems inherent to using the C preprocessor to access the API of a library.
Clang 以簡單的 import std.io
概念取代 本來冗餘的函數庫(libraries)引進 #include <stdio.h>
,相似java的package,目前Clang
#include的機制是 編譯器會去遞迴檢查每一個Header,header inculde的 header 文章裏提了幾個弱點: Compile-time scalability:耗時編譯 Fragility:多引入順序或致使宏衝突 Conventional workarounds:C語言長久的息慣,致使代碼較醜 Tool confusion
然而編譯器在碰到import時,會直接載入module對應的二進制文件並取得他的api,一個module不依賴外部header,只編譯一次,api也只解析一次, 固然module也有些缺點包括 namesspace(可能重名), 改庫代碼, 沒法適應各類機器的Arch。
此步驟是Compiler裏的基本程序,將字符一個一個的讀進Lexer裏,並根據構詞規則識別 Token(單詞),此處還不會校驗語法
作詞法分析並把Token分析結果展現出來
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
每個標記都包含了對應的源碼內容和其在源碼中的位置。注意這裏的位置是宏展開以前的位置,這樣一來,若是編譯過程當中遇到什麼問題,clang 可以在源碼中指出出錯的具體位置。
-fsyntax-only
: Run the preprocessor, parser and type checking stages.
語法分析,在Clang中有Parser和Sema兩個模塊配合完成,驗證語法是否正確,並給出正確的提示。
遍歷每一個Token作詞句分析,生成一個 節點(Nodes)該有的資訊
在Lex 跟 syntax Analysis以後, 也就是在這個階段已經確保 詞 句 語法已是正確的形式了,semantic 接着作return values, size boundaries, uninitialized variables 等檢查,以後根據當前的資訊,生成語意節點(Nodes),並將全部節點組合成抽象語法書(AST)
作 語法分析 並展現 AST
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
能夠說是Clang的核心,大部分的優化, 判斷都在AST處理(例如尋找Class, 替換代碼...等)
此步驟會將 Clang Attr 轉換成 AST 上的 AttributeList,能在clang插件上透過 Decl::getAttr<T>
獲取
Clang Attributes 是 Clang 提供的一種源碼註解,方便開發者向編譯器表達某種要求,參與控制如 Static Analyzer、Name Mangling、Code Generation 等過程, 通常以
__attribute__(xxx)
的形式出如今代碼中, Ex: NS_CLASS_AVAILABLE_IOS(9_0)
結構跟其餘Compiler的AST相同與其餘編譯器不一樣的是 Clang的AST是由C++構成相似Class,Variable的層級表示,其餘的則是以彙編語言編寫。
這表明着AST也能有對應的api,這讓AST操做, 獲取信息 都比較容易,甚至還夾帶着地址跟代碼位置。
AST Context: 存儲全部AST相關資訊, 且提供ASTMatcher等遍歷方法
Node三大Class Decl - Declarations(聲明), Stmt - Statements(陳述句), type(類型)
子類過於詳細不在這多寫
CodeGen負責將語法樹從頂至下遍歷,翻譯成LLVM IR,是LLVM Backend 的輸入,是先後端的橋接語言。
產出IR: clang -S -fobjc-arc -emit-llvm main.m -o main.ll
LLVM IR 有三種表示格式,第一種是 bitcode 這樣的存儲格式,以 .bc 作後綴,第二種是可讀的以 .ll,第三種是用於開發時操做 LLVM IR 的內存格式。
產出Bit clang -emit-llvm -c main.m -o main.bc
查看BitCode llvm-dis < main.bc | less
IR提供了多種優化選項,-01 -02 -03 -0s.... 對應着不一樣的入參,有好比相似死代碼清理,內聯化,表達式重組,循環變量移動這樣的 Pass。
能夠改變 clang 生成代碼的方式,增長更強的類型檢查,或者按照本身的定義進行代碼的檢查分析等等。要想達成以上的目標,
reference:
Clang插件 瞭解Clang-ast Understanding the Clang AST AST Detail ClangAST clang.llvm.org/ # 深刻剖析-iOS-編譯-Clang llvm.org/devmtg/2017… # 從Swift橋接文件到Clang-LLVM