(Xcode) 編譯器小白筆記 - LLVM前端Clang

本文爲筆記型式呈現,並不是所有原創,來源見文末html

Compiler

Three-Phase編譯器架構

Clang - LLVM

Apple(包括中後期的NeXT) 一直使用GCC做爲官方的編譯器。GCC做爲開源世界的編譯器標準一直作得不錯,但Apple對編譯工具會提出更高的要求。前端

Clang這個軟體專案在2005年由蘋果電腦發起,是LLVM編譯器工具集的前端(front-end),目的是輸出程式碼對應的抽象語法樹(Abstract Syntax Tree, AST),並將程式碼編譯成LLVM Bitcode。接着在後端(back-end)使用LLVM編譯成平臺相關的機器語言 。java

MDCC2016的 session

先看結果

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.mobjective-c

產出 .out (executable)後端

clang

Clang (Frontend前端)

是一個C、C++、Objective-C和Objective-C++程式語言的編譯器前端api

Clang源碼結構

source:https://llvm.org/devmtg/2017-06/2-Hal-Finkel-LLVM-2017.pdf

Clang步驟

clang -ccc-print-phases main.msession

Clang-LLVM步驟

source: https://blog.csdn.net/u014795020/article/details/72514109

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)

參考 LLVM 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

  1. #include的機制是 編譯器會去遞迴檢查每一個Header,header inculde的 header 文章裏提了幾個弱點: Compile-time scalability:耗時編譯 Fragility:多引入順序或致使宏衝突 Conventional workarounds:C語言長久的息慣,致使代碼較醜 Tool confusion

  2. 然而編譯器在碰到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 可以在源碼中指出出錯的具體位置。

展現Token(部分)

-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

AST

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(類型) 子類過於詳細不在這多寫

source: https://blog.csdn.net/u014795020/article/details/72514109

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。

project Build Setting

http://clang.llvm.org/docs/CommandGuide/clang.html#code-generation-options

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

相關文章
相關標籤/搜索