iOS底層探索(二) - 寫給小白看的Clang編譯過程原理

iOS底層探索(一) - 從零開始認(rèn)識Clang與LLVM

寫在前面

編譯器是屬于底層知識,在日常開發(fā)中少有涉及,但在我的印象中,越接近底層是越需要編程基本功,也是越復(fù)雜的。但要想提升技術(shù)卻始終繞不開要對底層原理的探究,很多資料都是直接拋出一堆函數(shù)概念和一頓操作,基礎(chǔ)一般的小伙伴看了表示一臉懵逼。在此結(jié)合我自己的理解進(jìn)行優(yōu)化總結(jié)一下。畢竟知識水平有限,有問題或總結(jié)不妥的地方歡迎指出,多多學(xué)習(xí),非常感謝!2018.2

入門起步

  • 經(jīng)過上一篇對編譯器的基本介紹,相信大家對Clang都有一個基本的認(rèn)識了,通俗來說是一個編譯器的前端,負(fù)責(zé)分析源代碼(就是我們使用的C/OC/C++等)。

Clang的編譯過程

1.預(yù)處理

  • 預(yù)處理顧名思義是預(yù)先處理,那預(yù)處理都做了哪些事情呢?內(nèi)容如下。

  • (1) import 頭文件替換

    • 面向?qū)ο缶幊痰乃季S下,我們寫代碼會經(jīng)常用到其他類的屬性\方法等,我們只需要導(dǎo)入頭文件就可以用了,如:

      #import <Foundation/Foundation.h> 
      // 這里將會在預(yù)處理時會把 Foundation.h 文件的內(nèi)容拷貝過來并替換
      
    • 基于這個原理,這里引出了一個小問題,如果 ClassA.h 文件引用了 ClassB.h ,并且 ClassB.h 也引用了 ClassA.h ,這里是不是就會互相循環(huán)引入了?

      • 解決辦法是在頭文件中使用
      @class ClassA;
      
      • 代替
      #import "ClassA.h"
      
      • 這么寫意思是聲明 ClassA 是一個類,這樣你就可以使用ClassA做類名了,如果需要使用 ClassA 的方法屬性等可以在 .m 實(shí)現(xiàn)文件中再通過 import MyClass.h 的方式使用,這種方法不但可以解決互相引入的問題還可以優(yōu)化編譯速度。
  • (2) macro 宏展開

    • 無參宏: 如:

      #define DATA_TYPE_NUM @"number"
      

      在此宏定義作用域內(nèi),輸入了 DATA_TYPE_NUM,在預(yù)處理過程中 DATA_TYPE_NUM 都會被替換成 @"number"。

    • 帶參宏: 帶參數(shù)的宏 如:

      #define CYXColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
      
  • (3) 處理其他的預(yù)編譯指令(其實(shí)預(yù)編譯過程也是出了預(yù)編譯指令的過程)

    條件編譯語句也是在預(yù)處理階段完成,并且條件編譯只允許編譯源程序中滿足條件的程序段,使生成的目標(biāo)程序較短,從而減少了內(nèi)存的開銷并提高了程序的效率,如以下代碼就只會保留一個return語句:

    #if DEBUG        
         return YES;
    #else
         return NO;
    #endif
    
  • (4) 總結(jié):

    • 簡單來說,“#”這個符號是編譯器預(yù)處理的標(biāo)志, 以下是一些常用的預(yù)處理指令參考
    預(yù)處理指令 用法解析
    #undef 取消已定義的宏
    #if 如果給定條件為真,則編譯以下代碼
    #ifdef 如果宏已經(jīng)定義,則編譯以下代碼
    #ifndef 如果宏沒有定義,則編譯以下代碼
    #elif 如果前面的#if給定條件不為真,當(dāng)前條件為真,則編譯以下代碼
    #endif 結(jié)束一個#if……#else條件編譯塊
*PS:還需要了解更多關(guān)于預(yù)編譯的內(nèi)容,還請自行百度*
[圖片上傳失敗...(image-cf6f6f-1531632712782)][圖片上傳失敗...(image-fd9112-1531632712782)]


`$clang -E main.m`

2. Lexical Analysis - 詞法分析(輸出token流)

  • 預(yù)處理完成了以后,開始詞法分析。詞法分析其實(shí)是編譯器開始工作真正意義上的第一個步驟,其所做的工作主要為將輸入的代碼轉(zhuǎn)換為一系列符合特定語言的詞法單元,這些詞法單元類型包括了關(guān)鍵字,操作符,變量等等。舉個例子:

Objective-C語言包含了關(guān)鍵字if、else、new等,那么在詞法分析步驟時,遇到i與f或n與e與w組合在一起的時候,需要將這幾個字母組合為關(guān)鍵字if或new這個詞法單元。

  • 詞法分析,只需要將源代碼以字符文本的形式轉(zhuǎn)化成Token流的形式,不涉及交驗(yàn)語義,不需要遞歸,是線性的。

    什么是token流呢?可以這么理解:就是有"類型",有"值"的一些小單元。

  • 再舉個例子:

    比如一個運(yùn)算表達(dá)式:(28 + 78) * 2 這里面只需要解析出(是一個開括號,28 是數(shù)字整形,+ 是一個運(yùn)算符號即可。

編譯指令: $clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m

Snip20171231_7.png

Snip20171231_4.png

3.Semantic Analysis - 語法分析(輸出(AST)抽象語法樹)

編譯指令:$clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

Snip20180122_2.png

  • 語法分析的最終產(chǎn)物是輸出抽象語法樹
  • 語法分析,在Clang中由Parser和Sema兩個模塊配合完成
  • 交驗(yàn)語法是否正確
  • 根據(jù)當(dāng)前語言的語法,生成語意節(jié)點(diǎn),并將所有節(jié)點(diǎn)組合成抽象語法樹(AST)
  • 這一步跟源碼等價,可以反寫出源碼
  • Static Analysis 靜態(tài)分析
    • 通過語法樹進(jìn)行代碼靜態(tài)分析,找出非語法性錯誤
    • 模擬代碼執(zhí)行路徑,分析出control-flow graph(CFG) 【MRC時代會分析出引用計數(shù)的錯誤】
    • 預(yù)置了常用Checker(檢查器)

未完待續(xù) ...

這是上篇,為保證博客質(zhì)量與閱讀體驗(yàn)(個人感覺一次閱讀過多文字有點(diǎn)影響閱讀體驗(yàn)),先分享已完成的上半部分,下篇將繼續(xù)介紹Clang編譯過程中的剩下環(huán)節(jié),歡迎持續(xù)關(guān)注,感謝理解與支持!2018.2

預(yù)告:下篇將繼續(xù)介紹Clang與LLVM以下環(huán)節(jié)的相關(guān)知識。

下面是一些關(guān)鍵詞,有興趣的朋友先自行谷歌學(xué)習(xí)吧,下篇等我有閑情的時候再更新了,我也不知道什么時候。2018.7.15

4. CodeGen - (Intermediate Representation,簡稱IR)IR中間代碼生成

  • CodeGen 負(fù)責(zé)將語法樹叢頂至下遍歷,翻譯成LLVM IR
  • LLVM IR 是Frontend的輸出,也是LLVM Backend的輸入,前后端的橋接語言 (Swift也是轉(zhuǎn)成這個)
  • 與 Objective-C Runtime 橋接
    • Class/Meta Class/Protocol/Category內(nèi)存結(jié)構(gòu)生成,并存放在指定section中(如Class:_DATA, _objc_classrefs)
    • Method/lvar/Property內(nèi)存結(jié)構(gòu)生成
    • 組成method_list/ivar_list/property_list并填入Class
    • Non-Fragile ABI:為每個Ivar合成OBJC_IVAR_$_偏移值常量
    • 存取Ivar的語句(ivar = 123; int a = ivar;)轉(zhuǎn)寫成base + OBJC_IVAR$_的形式
    • 將語法樹中的ObjcMessageExpr翻譯成相應(yīng)版本的objc_msgSend,對super關(guān)鍵字的調(diào)用翻譯成objc_msgSendSuper
    • 根據(jù)修飾符strong/weak/copy/atomic合成@property 自動實(shí)現(xiàn)的 setter/getter
    • 處理@synthesize
    • 生成block_layout的數(shù)據(jù)結(jié)構(gòu)
    • 變量的capture(__block/__weak)
    • 生成_block_invoke函數(shù)
    • ARC:分析對象引用關(guān)系,將objc_storeStrong/objc_storeWeak等ARC代碼插入
    • 將ObjCAutoreleasePoolStmt轉(zhuǎn)譯成objc_autoreleasePoolPush/Pop
    • 實(shí)現(xiàn)自動調(diào)用[super dealloc]
    • 為每個擁有ivar的Class合成.cxx_destructor方法來自動釋放類的成員變量,代替MRC時代的“self.xxx = nil”

5. Optimize - 優(yōu)化IR

  • 遞歸優(yōu)化成尾遞歸

6. LLVM Bitcode - 生成字節(jié)碼

7. Assemble - 生成Target相關(guān)匯編

  • Assemble - 生成Target相關(guān)Object(Mach-O)

8. Link生成Executable

參考文檔

https://zh.wikipedia.org/wiki/C%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8
https://llvm.org/docs/tutorial/LangImpl2.html
https://www.objc.io/issues/6-build-tools/compiler/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內(nèi)容