本文為筆記型式呈現,並非全部原創,來源見文末
Compiler
Clang - LLVM
Apple(包括中後期的NeXT) 一直使用GCC作為官方的編譯器。GCC作為開源世界的編譯器標準一直做得不錯,但Apple對編譯工具會提出更高的要求。
Clang這個軟體專案在2005年由蘋果電腦發起,是LLVM編譯器工具集的前端(front-end),目的是輸出程式碼對應的抽象語法樹(Abstract Syntax Tree, AST),並將程式碼編譯成LLVM Bitcode。接著在後端(back-end)使用LLVM編譯成平臺相關的機器語言 。
先看結果
main.m
#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;
}
複製程式碼
直接編譯成執行檔
clang -fmodules main.m
產出 .out (executable)
Clang (Frontend前端)
是一個C、C++、Objective-C和Objective-C++程式語言的編譯器前端
Clang原始碼結構
Clang步驟
clang -ccc-print-phases main.m
1.Input (Driver)
指定語言 , 架構, 輸入file
clang -x objective-c main.m
2.Preprocessor(預處理)
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
2.1 Modules 模組 (-fmodules)
文章裡提及: 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。
3 Lexical Analysis (詞法分析 Lex, Tokenization) -> .i (Tokens)
此步驟是Compiler裡的基本程式,將字元一個一個的讀進Lexer裡,並根據構詞規則識別 Token(單詞),此處還不會校驗語法
做詞法分析並把Token分析結果展示出來
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
每一個標記都包含了對應的原始碼內容和其在原始碼中的位置。注意這裡的位置是巨集展開之前的位置,這樣一來,如果編譯過程中遇到什麼問題,clang 能夠在原始碼中指出出錯的具體位置。
-fsyntax-only
: Run the preprocessor, parser and type checking stages.
4 語法分析(Semantic Analysis) -> AST
語法分析,在Clang中有Parser和Sema兩個模組配合完成,驗證語法是否正確,並給出正確的提示。
4.1 Parser
遍歷每個Token做詞句分析,生成一個 節點(Nodes)該有的資訊
4.2 Semantic
在Lex 跟 syntax Analysis之後, 也就是在這個階段已經確保 詞 句 語法已經是正確的形式了,semantic 接著做return values, size boundaries, uninitialized variables 等檢查,之後根據當前的資訊,生成語意節點(Nodes),並將所有節點組合成抽象語法書(AST)
做 語法分析 並展示 AST
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
5 抽象語法樹 Abstract Syntax Tree
可以說是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(型別)
子類過於詳細不在這多寫
6 程式碼生成 CodeGen -> IR中間程式碼(.ll)
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
6.1 IR 優化 Optimization
IR提供了多種優化選項,-01 -02 -03 -0s.... 對應著不同的入參,有比如類似死程式碼清理,內聯化,表示式重組,迴圈變數移動這樣的 Pass。
Extra: Clang 外掛
使用 libclan g, clang, LibTooling 外掛
可以改變 clang 生成程式碼的方式,增加更強的型別檢查,或者按照自己的定義進行程式碼的檢查分析等等。要想達成以上的目標,
reference:
Clang外掛 瞭解Clang-ast Understanding the Clang AST AST Detail ClangAST clang.llvm.org/ # 深入剖析-iOS-編譯-Clang llvm.org/devmtg/2017… # 從Swift橋接檔案到Clang-LLVM