今天我們來對iOS開發(fā)的常用工具Xcode的編譯流程進行一個簡單的了解和分析
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進行降級簡化成更加簡潔的中間代碼版本
swift的編譯器前端是swiftc搓蚪,于Clang相比吐葵,LLVM的前端編譯過程中吁断,AST和IR之間,多了一層SIL(Swift Intermediate Language)殉农,這么做的目的是希望彌補clang編譯器的一些缺陷,比如無法執(zhí)行一些高級分析,可靠的診斷和優(yōu)化吱晒,而 AST 和LLVM IR 都不是合適的選擇财著。因此联四,SIL應(yīng)運而生,用來解決現(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ǔ)上將單詞序列組合成語法樹杜跷,如“程序”傍念,“語句”,“表達式”等等.語法分析程序判斷源程序在結(jié)構(gòu)上是否正確葛闷。源程序的結(jié)構(gòu)由上下文無關(guān)文法描述憋槐。
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
通過終端進入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
- @_hasStorage表示的是儲存屬性
- @_hasInitialValue表示的是具有初始值
- @sil_global表示的是全局變量
- 一個析構(gòu)方法deinit
- 一個初始化函數(shù)init()
這里聲明了Teacher類,并定義了一個全局的person屬性扣泊,屬于Teacher類
2.看下main.sil文件中的main 函數(shù)
每個程序的開始都是main函數(shù)近范,swift語言也不例外,但是swift中的main函數(shù)被隱藏了延蟹,main.swift文件就代表了整個main函數(shù)评矩,在文件里寫的代碼會在main中運行
// main函數(shù),相當于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'
- @main 這里是標示我們當前main.swift的入口函數(shù)师崎,SIL中的標示符以 @作為前綴
- %0默终,%1……在SIL也叫寄存器,這里我們可以理解為我們?nèi)粘i_發(fā)的常量,一旦賦值之后就不可以在修改齐蔽,如果SIL中還要繼續(xù)使用两疚,那么就不斷的累加數(shù)字
- alloc_global是創(chuàng)建一個全局變量
- global_addr是拿到全局變量的地址,賦值給%3
- metatype是拿到Teacher的Metaldata賦值給%4
- 接下來就是將__allocating_init()的函數(shù)地址賦值給%5
- apply是調(diào)用函數(shù)含滴,這里是調(diào)用%5也就是__allocating_init()诱渤,%4是參數(shù),并將返回值給%6
- 然后將%6的值存儲到%3谈况,也就是我們剛剛創(chuàng)建的全局變量的地址
- 然后是構(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