從Swift橋接文件到Clang-LLVM

http://blog.csdn.net/u014795020/article/details/72514109javascript

前言

今天在Swift工程中不當心建立了一個OC文件,因而乎提示我建立一個橋接文件,那麼爲何須要建立橋接文件呢,它的原理又是什麼呢?html

打開百度一搜,全是教你怎麼建立橋接文件的,彷佛找不到答案~前端

swift橋接文件原理搜索結果

LVVM - Low Level Virtual Machine 
Clang - C Lange Family Frontend for LVVMjava

編譯器探究

  • GCC

GNU編譯器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、Java、Ada和Go語言的前端,也包括了這些語言的庫(如libstdc++、libgcj等等)node

早起的OC 程序員都感覺過GCC編譯程序,可是蘋果爲何好好的GCC不用,本身要搞一套呢?python

1.GCC 的 objective-c Frontend不給力:GCC的前端不是蘋果提供維護的,想要添加一些語法提示等功能還得去求GCC的前端去作。ios

2.GCC 插件、工具、IDE的支持薄弱:不少編譯器特性沒有,自動補全、代碼提示、warning、靜態分析等這些流程不是很給力,都是須要IDE調用底層命令完成的,結果須要以插件的形式暴露出來,這一塊GCC作的不是很好。c++

3.GCC 編譯效率和性能不足:Apple的Clang出來之後,其編譯效率是GCC的3倍,編譯器性能好,編譯出的文件小。git

4.Apple要收回去工具鏈的控制 (lldb, lld…): Apple在早起從GCC前端到LLVM後端的編譯器,到Clang-LVVM的編譯器,之後後來的GDB的替換,一步一步收回對編譯工具鏈的控制,也爲swift 的出現奠基基礎。程序員

  • Three-Phase 編譯器架構

Three-Phase編譯器架構

上圖是最簡單的三段式編譯器架構

首先,咱們看到source 是咱們的源代碼,進入編譯器的前端Frontend;在前端完成以後,就進入優化器這一模塊;優化完成以後進入後端這一模塊;在這所有完成以後,根據你的架構是x86,armv7等生產機器碼。

可是會有一個問題:

M (Language) * N (Target) = M * N (Compilers)
  • 1
  • 1

就是若是你有M種語言(C、C++、Objective-C…),N種架構(armv七、armv7s、arm6四、i38六、x86_64…),那麼你就有M * N中編譯方式須要處理,顯然是不合理的。

  • apple 的 Clang/Swift - LLVM 編譯器架構:

這裏寫圖片描述

其中優化器部分(Common Optimizer)是共享的。而對於每一種語言都有其前端部分,假如新有一門語言,只須要實現該語言的前端模塊;若是新出一臺設備,它的架構不一樣,那麼也只須要單獨完成其後端模塊便可。改動很是小,不會作重複的工做。

下面詳解:

Clang/Swift - LLVM 編譯器架構

藍色的部分:C語言家族系列的前端,屬於Clang部分。

綠色的部分: Swift語言的前端,其中還包含本身的SIL中間語言和Optimzer中間語言的優化過程。

紫色的部分: 優化階段和後端模塊統一是LLVM部分。

  • 代碼規模

Clang + LLVM 代碼模塊總共有400W行代碼,其中主體部分是C++寫的,大概有235W行。若是將全部的target,lib等文件編譯出來,大概有近20G的大小:

這裏寫圖片描述

對比Swift Frontend 代碼規模,就少不少,只有43W行左右。可能在後端,好比優化器策略,生成機器碼部分就有不少代碼:

這裏寫圖片描述

  • Clang命令

Clang在概念上是編譯器前端,同時,在命令行中也做爲一個「黑盒」的 Driver;

它封裝了編譯管線、前端命令、LLVM命令、Toolchain命令等,即一個Clang走天下;

方便從GCC遷移過來。

當咱們點擊run命令之後,以下圖:

這裏寫圖片描述

就是咱們在build setting中的一些設置,組裝成命令,下面能夠看到是一個 oc文件在arc環境下的編譯過程:

這裏寫圖片描述

  • 拆解編譯過程
#import <Foundation/Foundation.h> int main() { @autoreleasepool { id obj = [NSObject new]; NSLog(@"Hellow world: %@", obj); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.Preprocess - 預處理

import 頭文件,include頭文件等 
macro宏展開 
處理’#’大頭的預處理指令,如 #if,#elseif等

終端輸入:

$ clang -E main.m
  • 1
  • 1

只會作預處理步驟,不日後面走,以下

這裏寫圖片描述

能夠看到一個頭文件要導入不少行代碼,這裏就要說到pch文件。自己Apple給出這個文件,是讓咱們放入Foundation或者UIKit等這些根本不會變的庫,優化編譯過程,可是開發者卻各類宏,各類頭文件導入,致使編譯速度很慢。以致於後來蘋果刪除了這個文件,只能開發者本身建立。可是蘋果除了modules這個概念,能夠經過如下命令打開:

$ clang -E -fmodules main.m
  • 1
  • 1

默認把一些文件打包成庫文件, 在build setting中默認打開的,咱們能夠用@import Foundation:

這裏寫圖片描述

2.Lexical Analysis - 詞法分析

詞法分析,也做Lex 或者 Tokenization 
將預處理過得代碼文本轉化爲Token流 
不會校驗語義

能夠在終端輸入如下命令:

$ clang -fmodules -fayntax-only -Xclang -dump-tokens main.m
  • 1
  • 1

以下圖: 
這裏寫圖片描述

3.Analysis - 語法分析

語法分析,在Clang中有Parser和Sema兩個模塊配合完成,驗證語法是否正確,並給出正確的提示。這就是Clang標榜GCC,本身的語法提示友好的體現。

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

輸入命令:

$ clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
  • 1
  • 1

以下圖: 
這裏寫圖片描述

能夠經過語法樹,反寫回源碼,以下圖:

這裏寫圖片描述

4.Static Analysis - 靜態分析(不是必須的)

經過語法書進行代碼靜態分析,找出非語法性錯誤 
模擬代碼執行路徑,分析出 contro-flow graph (CFG) 
預置了經常使用的 Checker

在Xcode中以下操做能夠實現: 

這裏寫圖片描述

5.CodeGen - IR 代碼生成

CodeGen負責將語法樹從頂至下遍歷,翻譯成LLVM IR,LLVMIR 是Frontend的輸入,也是LLVM Backend 的輸入,是先後端的橋接語言。

與Objective-C Runtime 橋接

①Class / Meta Class / Protocol / Category 內存結構生成,並存放在指定 session中(如Class: _DATA, _objc_classrefs)

②Method / Ivar / 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

⑦處理@synthsynthesize

⑧生成 block_layout 的數據結構

⑨變量 capture (__block/ __weak)

10.生成_block_invoke函數

11.ARC: 分析對象引用關係,將 objc_storeStrong/ objc_storeWeak 等ARC 代碼插入

12.將 ObjCAutoreleasePoolStmt 轉譯成objc_autoreleasePoolPush/Pop

13.實現自動調用[super dealloc]

14.爲每一個擁有 ivar 的Class 合成.cxx_destructor 方法來自動釋放類的成員變量,代替MRC 時代下的」self.xxx = nil」

舉個栗子,嘿嘿: 
這裏寫圖片描述

終端輸入:

$ clang -S -fobjc-arc -emit-llvm main.m -o main.m
  • 1
  • 1

輸入以下: 
這裏寫圖片描述

介於C和彙編的中間形態。

若是加入優化:

$ clang -O3 -S -fobjc-arc -emit-llvm main.m -o main.m
  • 1
  • 1

這裏寫圖片描述

明顯感受少了不少。

6. LVVM Bitcode - 生成字節碼

輸入命令:

$ clang -emit-llvm -c main.m -o main.bc
  • 1
  • 1

這裏寫圖片描述

相信你們在iOS 9以後都聽過這個概念,其實就是對IR生成二進制的過程。

7.Assemble - 生成Target相關彙編

終端輸入:

$ clang -S -fobjc-arc main.m -o main.s
  • 1
  • 1

以下圖:

這裏寫圖片描述

彙編代碼。

8.Assemble - 生成Target相關 Object(Mach-o) 
終端輸入:

$ clang -fmodules -c main.m -o main.o
  • 1
  • 1

這裏寫圖片描述

彙編的main.o的形式。

9.Link 生成 Executable

終端輸入:

$ clang main.m -o main $ ./main
  • 1
  • 2
  • 1
  • 2

這裏寫圖片描述

總結一下吧: 
這裏寫圖片描述

至此,我猜想可能橋接文件是在Clang階段,將OC文件進行編譯,生成語法樹,而後再返成Swift能識別的類文件。

  • 咱們能在Clang上作什麼?

Apple給咱們留了3個接口:

1.LibClang 
功能: 
①C 的API來訪問Clang的上層能力,好比獲取Tokens、遍歷語法樹、代碼補全、獲取診斷信息; 
②API穩定,不受Clang源碼更新影響 
③只有上層的語法樹能夠訪問,不能獲取到所有信息 
使用: 
④使用原始的 C的API 
⑤腳本語言: 使用官方提供的 Python binding 或開源的 node-js / ruby binding 
⑥Objective-C: 開源庫 ClangKit

2.LibTooling 
①對語法樹 有徹底的控制權 
②可做爲一個 standalone 命令單獨使用,如 clang-format 
③須要使用C++且對Clang源碼熟悉

3.ClangPlugin 
①對語法樹有徹底的控制權 
②做爲插件注入到編譯流程中,能夠影響build和決定編譯過程 
③須要使用C++且對Clang源碼熟悉

參考資料: 
http://clang.llvm.org/docs/index.html 
http://blog.llvm.org/ 
https://www.objc.io/issues/6-build-tools/compiler/ 
http://llvm.org/docs/tutorial/index.html 
https://github.com/loarabia/Clang-tutorial 
http://lowlevelbits.org/getting-started-with-llvm/clang-on-os-x/ 
https://hevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-i-introducation/ 
http://szelei.me/code-generator/

    • 《Getting Started with LLVM Core Libraries》
    • 《LLVM Cookbook》
相關文章
相關標籤/搜索