Xcode的編譯過程

今天我們來對iOS開發(fā)的常用工具Xcode的編譯流程進(jìn)行一個簡單的了解和分析

image

OC:source code > Clang -> LLVM -> Backend -> Executable
Clang做的事情是詞法分析-> token流->語法分析-> AST -> LLVM IR

AST(Abstract Syntax Tree 抽象語法樹)
IR(intermediate representation 中間代碼)


swift:source code > swiftc -> LLVM -> Backend -> Executable
swiftc:swift AST -> Raw Swift IL -> Canonical Swift IL -> LLVM IR

Raw Swift IL : Swift特有的中間代碼
Canonical Swift IL:將Raw Swift IL進(jìn)行降級簡化成更加簡潔的中間代碼版本

swift的編譯器前端是swiftc,于Clang相比,LLVM的前端編譯過程中,AST和IR之間,多了一層SIL(Swift Intermediate Language),這么做的目的是希望彌補(bǔ)clang編譯器的一些缺陷,比如無法執(zhí)行一些高級分析,可靠的診斷和優(yōu)化,而 AST 和LLVM IR 都不是合適的選擇。因此,SIL應(yīng)運(yùn)而生,用來解決現(xiàn)有的缺陷


AST(Abstract Syntax Tree 抽象語法樹)

生成AST的過程

詞法分析(lexical analysis
也叫掃描器,讓源代碼的字符流根據(jù)構(gòu)詞規(guī)范生成token流

tokenize:tokenize就是按照一定的規(guī)則,例如token令牌(通常代表關(guān)鍵字,變量名,語法符號等),將代碼分割為一個個的“串”,也就是語法單元)。涉及到詞法解析的時候,常會用到tokennize。

語法分析(parse analysis)是編譯過程的一個邏輯階段。語法分析的任務(wù)是在詞法分析的基礎(chǔ)上將單詞序列組合成語法樹,如“程序”,“語句”,“表達(dá)式”等等.語法分析程序判斷源程序在結(jié)構(gòu)上是否正確。源程序的結(jié)構(gòu)由上下文無關(guān)文法描述。

image

Clang:

AST的轉(zhuǎn)換例子

@property (nonatomic, strong) NSString *haoyuString;

PS:屬性(Property)是Objective-C語言的其中一個特性,它把類對象中的實例變量及其讀寫方法統(tǒng)一的封裝

生成的AST如下:

 ObjCPropertyDecl 0x7f96f693a400 <line:13:1, col:41> col:41 haoyuString 'NSString *' readwrite nonatomic strong
 | |-ObjCMethodDecl 0x7f96f693a478 <col:41> col:41 implicit - haoyuString 'NSString *'
 | |-ObjCMethodDecl 0x7f96f693a4f8 <col:41> col:41 implicit - setHaoyuString: 'void'
 | | `-ParmVarDecl 0x7f96f693a578 <col:41> col:41 haoyuString 'NSString *'

常見的數(shù)據(jù)類型聲明在AST中表示

 NSString *str = @"hahahah"; //oc中賦值代碼

AST中的表示為:

 VarDecl 0x7fd06cc91ae8 <line:24:5, col:22> col:15 str 'NSString *__strong' cinit //聲明局部變量
 | | | `-ObjCStringLiteral 0x7fd06cc91ba8 <col:21, col:22> 'NSString *' //聲明變量類型
 | | |  `-StringLiteral 0x7fd06cc91b88 'char [8]' lvalue "hahahah" //右邊的字符串有char類型表示

String:StringLiteral
NSInteger ,Int: IntegerLiteral
Float: FloatingLiteral
Array: ObjCArrayLiteral
Dictionary : ObjCDictionaryLiteral

將源代碼生成語法樹 AST:

clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

Swiftc:

生成AST的方式和Clang類似,這里著重介紹下swiftc編譯器的SIL(Swift Intermediate Language )

1.生成的main.swift文件中編寫如下代碼

import Foundation
class Teacher {
    var age: Int = 18
    var name: String = "Tom"
}

var person = Teacher()
person.age = 6

通過終端進(jìn)入main.swift所在的文件夾,輸入如下指令:

swiftc -emit-sil main.swift  //生成了main.sil文件
// 打開`main.sil` 文件,首先看到了Teacher的聲明
class Teacher {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

@_hasStorage @_hasInitialValue var person: Teacher { get set }

// person
sil_global hidden @main.person : main.Teacher : $Teacher
  1. @_hasStorage表示的是儲存屬性
  2. @_hasInitialValue表示的是具有初始值
  3. @sil_global表示的是全局變量
  4. 一個析構(gòu)方法deinit
  5. 一個初始化函數(shù)init()

這里聲明了Teacher類,并定義了一個全局的person屬性,屬于Teacher類

2.看下main.sil文件中的main 函數(shù)

每個程序的開始都是main函數(shù),swift語言也不例外,但是swift中的main函數(shù)被隱藏了,main.swift文件就代表了整個main函數(shù),在文件里寫的代碼會在main中運(yùn)行

// main函數(shù),相當(dāng)于c程序入口函數(shù)int main(int argc, char * argv[])
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
//%0,%1……在SIL也叫寄存器,這里我們可以理解為我們?nèi)粘i_發(fā)的常量,
//一旦賦值之后就不可以在修改,如果SIL中還要繼續(xù)使用,那么就不斷的累加數(shù)字
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
//初始化全局變量person  alloc_global是創(chuàng)建一個全局變量
  alloc_global @main.person : main.Teacher       // id: %2
//創(chuàng)建對之前由alloc_global初始化的全局變量地址的引用
//global_addr是拿到全局變量的地址,賦值給%3
  %3 = global_addr @main.person : main.Teacher : $*Teacher // users: %7, %8
//元類型Teacher
//metatype是拿到Teacher的Metaldata賦值給%4
  %4 = metatype $@thick Teacher.Type              // user: %6
// 方法__allocating_init()的引用
//接下來就是將__allocating_init()的函數(shù)地址賦值給%5
  %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %6
//調(diào)用函數(shù)__allocating_init,并傳入?yún)?shù)元類型Teacher
//apply是調(diào)用函數(shù),這里是調(diào)用%5也就是__allocating_init(),%4是參數(shù),并將返回值給%6
  %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %7
//將函數(shù)__allocating_init的結(jié)果存入person的引用
//然后將%6的值存儲到%3,也就是我們剛剛創(chuàng)建的全局變量的地址
  store %6 to %3 : $*Teacher                      // id: %7
//開始訪問全局變量地址的引用
  %8 = begin_access [read] [dynamic] %3 : $*Teacher // users: %9, %11
//將內(nèi)容載入%9
  %9 = load %8 : $*Teacher                        // users: %16, %14, %15, %10
//引用計數(shù)加一
  strong_retain %9 : $Teacher                     // id: %10
//結(jié)束訪問
  end_access %8 : $*Teacher                       // id: %11
//創(chuàng)建字面量6
  %12 = integer_literal $Builtin.Int64, 6         // user: %13
//生成Int值6,swift中Int是結(jié)構(gòu)體
  %13 = struct $Int (%12 : $Builtin.Int64)        // user: %15
//Teacher.age的setter方法
  %14 = class_method %9 : $Teacher, #Teacher.age!setter : (Teacher) -> (Int) -> (), $@convention(method) (Int, @guaranteed Teacher) -> () // user: %15
//調(diào)用setter方法,傳入Int值6,和類實例本身
  %15 = apply %14(%13, %9) : $@convention(method) (Int, @guaranteed Teacher) -> ()
//引用計數(shù)減一
  strong_release %9 :
//創(chuàng)建字面量0
  %17 = integer_literal $Builtin.Int32, 0         // user: %18
//生成Int值0,swift中Int是結(jié)構(gòu)體
  %18 = struct $Int32 (%17 : $Builtin.Int32)      // user: %19
//最后將0從main函數(shù)中返回出去
  return %18 : $Int32                         // id: %16
} // end sil function 'main'
  1. @main 這里是標(biāo)示我們當(dāng)前main.swift的入口函數(shù),SIL中的標(biāo)示符以 @作為前綴
  2. %0,%1……在SIL也叫寄存器,這里我們可以理解為我們?nèi)粘i_發(fā)的常量,一旦賦值之后就不可以在修改,如果SIL中還要繼續(xù)使用,那么就不斷的累加數(shù)字
  3. alloc_global是創(chuàng)建一個全局變量
  4. global_addr是拿到全局變量的地址,賦值給%3
  5. metatype是拿到Teacher的Metaldata賦值給%4
  6. 接下來就是將__allocating_init()的函數(shù)地址賦值給%5
  7. apply是調(diào)用函數(shù),這里是調(diào)用%5也就是__allocating_init(),%4是參數(shù),并將返回值給%6
  8. 然后將%6的值存儲到%3,也就是我們剛剛創(chuàng)建的全局變量的地址
  9. 然后是構(gòu)建Int并return

3. 這里面有些固定搭配

1.初始化一個引用

//初始化全局變量person
  alloc_global @main.person : main.Teacher
//創(chuàng)建對之前由alloc_global初始化的全局變量地址的引用
  %3 = global_addr @main.person : main.Teacher 

2.調(diào)用一個方法

// 方法__allocating_init()的引用
  %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher
//調(diào)用函數(shù)__allocating_init,并傳入?yún)?shù)元類型Teacher
  %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher

3.獲得swfit的基本類型

//創(chuàng)建字面量6
  %12 = integer_literal $Builtin.Int64, 6
//生成Int值6,swift中Int是結(jié)構(gòu)體
  %13 = struct $Int (%12 : $Builtin.Int64)

swiftc命令:

生成可執(zhí)行文件:swiftc -o main.out main.swift

生成抽象語法樹的命令(AST):swiftc main.swift -dump-ast

生成中間語言(SIL):swiftc main.swift -emit-sil

LLVM中間表示層(LLVM IR):swiftc main.swift -emit -ir

生成匯編語言:swiftc main.swift -emit-assembly
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容