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

iOS底層探索(一) - 從零開始認識Clang與LLVMhtml

寫在前面

編譯器是屬於底層知識,在平常開發中少有涉及,但在個人印象中,越接近底層是越須要編程基本功,也是越複雜的。但要想提高技術卻始終繞不開要對底層原理的探究,不少資料都是直接拋出一堆函數概念和一頓操做,基礎通常的小夥伴看了表示一臉懵逼。在此結合我本身的理解進行優化總結一下。畢竟知識水平有限,有問題或總結不妥的地方歡迎指出,多多學習,很是感謝!2018.2前端

入門起步

  • 通過上一篇對編譯器的基本介紹,相信你們對Clang都有一個基本的認識了,通俗來講是一個編譯器的前端,負責分析源代碼(就是咱們使用的C/OC/C++等)。

Clang的編譯過程

1.預處理

  • 預處理顧名思義是預先處理,那預處理都作了哪些事情呢?內容以下。編程

  • (1) import 頭文件替換後端

    • 面向對象編程的思惟下,咱們寫代碼會常常用到其餘類的屬性\方法等,咱們只須要導入頭文件就能夠用了,如:bash

      #import <Foundation/Foundation.h> 
      // 這裏將會在預處理時會把 Foundation.h 文件的內容拷貝過來並替換
      複製代碼
    • 基於這個原理,這裏引出了一個小問題,若是 ClassA.h 文件引用了 ClassB.h ,而且 ClassB.h 也引用了 ClassA.h ,這裏是否是就會互相循環引入了?數據結構

      • 解決辦法是在頭文件中使用
      @class ClassA;
      複製代碼
      • 代替
      #import "ClassA.h"
      複製代碼
      • 這麼寫意思是聲明 ClassA 是一個類,這樣你就可使用ClassA作類名了,若是須要使用 ClassA 的方法屬性等能夠在 .m 實現文件中再經過 import MyClass.h 的方式使用,這種方法不但能夠解決互相引入的問題還能夠優化編譯速度。
  • (2) macro 宏展開函數

    • 無參宏: 如:學習

      #define DATA_TYPE_NUM @"number"
      複製代碼

      在此宏定義做用域內,輸入了 DATA_TYPE_NUM,在預處理過程當中 DATA_TYPE_NUM 都會被替換成 @"number"。優化

    • 帶參宏: 帶參數的宏 如:ui

      #define CYXColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
      複製代碼
  • (3) 處理其餘的預編譯指令(其實預編譯過程也是出了預編譯指令的過程)

    條件編譯語句也是在預處理階段完成,而且條件編譯只容許編譯源程序中知足條件的程序段,使生成的目標程序較短,從而減小了內存的開銷並提升了程序的效率,如如下代碼就只會保留一個return語句:

    #if DEBUG 
         return YES;
    #else
         return NO;
    #endif
    複製代碼
  • (4) 總結:

    • 簡單來講,「#」這個符號是編譯器預處理的標誌, 如下是一些經常使用的預處理指令參考
    預處理指令 用法解析
    #undef 取消已定義的宏
    #if 若是給定條件爲真,則編譯如下代碼
    #ifdef 若是宏已經定義,則編譯如下代碼
    #ifndef 若是宏沒有定義,則編譯如下代碼
    #elif 若是前面的#if給定條件不爲真,當前條件爲真,則編譯如下代碼
    #endif 結束一個#if……#else條件編譯塊
*PS:還須要瞭解更多關於預編譯的內容,還請自行百度*
[圖片上傳失敗...(image-cf6f6f-1531632712782)][圖片上傳失敗...(image-fd9112-1531632712782)]


`$clang -E main.m`
複製代碼

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

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

Objective-C語言包含了關鍵字if、else、new等,那麼在詞法分析步驟時,遇到i與f或n與e與w組合在一塊兒的時候,須要將這幾個字母組合爲關鍵字if或new這個詞法單元。

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

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

  • 再舉個例子:

    好比一個運算表達式:(28 + 78) * 2 這裏面只須要解析出(是一個開括號,28 是數字整形,+ 是一個運算符號便可。

編譯指令: $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

  • 語法分析的最終產物是輸出抽象語法樹

  • 語法分析,在Clang中由Parser和Sema兩個模塊配合完成

  • 交驗語法是否正確

  • 根據當前語言的語法,生成語意節點,並將全部節點組合成抽象語法樹(AST)

  • 這一步跟源碼等價,能夠反寫出源碼

  • Static Analysis 靜態分析

    • 經過語法樹進行代碼靜態分析,找出非語法性錯誤
    • 模擬代碼執行路徑,分析出control-flow graph(CFG) 【MRC時代會分析出引用計數的錯誤】
    • 預置了經常使用Checker(檢查器)

未完待續 ...

這是上篇,爲保證博客質量與閱讀體驗(我的感受一次閱讀過多文字有點影響閱讀體驗),先分享已完成的上半部分,下篇將繼續介紹Clang編譯過程當中的剩下環節,歡迎持續關注,感謝理解與支持!2018.2

預告:下篇將繼續介紹Clang與LLVM如下環節的相關知識。

下面是一些關鍵詞,有興趣的朋友先自行谷歌學習吧,下篇等我有閒情的時候再更新了,我也不知道何時。2018.7.15

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

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

5. Optimize - 優化IR

  • 遞歸優化成僞遞歸

6. LLVM Bitcode - 生成字節碼

7. Assemble - 生成Target相關彙編

  • Assemble - 生成Target相關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/

相關文章
相關標籤/搜索