本日我们来对iOS开发的常用工具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编译器的一些缺陷,好比无法实验一些高级分析,可靠的诊断和优化,而 AST 和LLVM IR 都不是合适的选择。因此,SIL应运而生,用来办理现有的缺陷
AST(Abstract Syntax Tree 抽象语法树)
天生AST的过程
词法分析(lexical analysis)
也叫扫描器,让源代码的字符流根据构词规范天生token流
tokenize:tokenize就是按照肯定的规则,比方token令牌(通常代表关键字,变量名,语法符号等),将代码分割为一个个的“串”,也就是语法单位)。涉及到词法分析的时间,常会用到tokennize。
语法分析(parse analysis)是编译过程的一个逻辑阶段。语法分析的使命是在词法分析的根本上将单词序列组合成语法树,如“步伐”,“语句”,“表达式”等等.语法分析步伐判定源步伐在结构上是否精确。源步伐的结构由上下文无关文法形貌。
Clang:
AST的转换例子
@property (nonatomic, strong) NSString *haoyuStringS:属性(Property)是Objective-C语言的此中一个特性,它把类对象中的实例变量及其读写方法同一的封装
天生的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 *'常见的数据范例声明在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.mSwiftc:
天生AST的方式和Clang类似,这里偏重先容下swiftc编译器的SIL(Swift Intermediate Language )
1.天生的main.swift文件中编写如下代码
import Foundationclass 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 }// personsil_global hidden @main.person : main.Teacher : $Teacher
- @_hasStorage表现的是储存属性
- @_hasInitialValue表现的是具有初始值
- @sil_global表现的是全局变量
- 一个析构方法deinit
- 一个初始化函数init()
这里声明白Teacher类,并界说了一个全局的person属性,属于Teacher类
2.看下main.sil文件中的main 函数
每个步伐的开始都是main函数,swift语言也不例外,但是swift中的main函数被潜伏了,main.swift文件就代表了整个main函数,在文件里写的代码会在main中运行
// main函数,相当于c步伐入口函数int main(int argc, char * argv[])sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {//%0,%1……在SIL也叫寄存器,这里我们可以明白为我们一样寻常开发的常量,//一旦赋值之后就不可以在修改,如果SIL中还要继承利用,那么就不停的累加数字bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>)://初始化全局变量person alloc_global是创建一个全局变量 alloc_global @main.person : main.Teacher // id: %2//创建对之前由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()的函数地点赋值给%5 %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %6//调用函数__allocating_init,并传入参数元范例Teacher//apply是调用函数,这里是调用%5也就是__allocating_init(),%4是参数,并将返回值给%6 %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %7//将函数__allocating_init的结果存入person的引用//然后将%6的值存储到%3,也就是我们刚刚创建的全局变量的地点 store %6 to %3 : $*Teacher // id: %7//开始访问全局变量地点的引用 %8 = begin_access [read] [dynamic] %3 : $*Teacher // users: %9, %11//将内容载入%9 %9 = load %8 : $*Teacher // users: %16, %14, %15, %10//引用计数加一 strong_retain %9 : $Teacher // id: %10//竣事访问 end_access %8 : $*Teacher // id: %11//创建字面量6 %12 = integer_literal $Builtin.Int64, 6 // user: %13//天生Int值6,swift中Int是结构体 %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//调用setter方法,传入Int值6,和类实例自己 %15 = apply %14(%13, %9) : $@convention(method) (Int, @guaranteed Teacher) -> ()//引用计数减一 strong_release %9 ://创建字面量0 %17 = integer_literal $Builtin.Int32, 0 // user: %18//天生Int值0,swift中Int是结构体 %18 = struct $Int32 (%17 : $Builtin.Int32) // user: %19//末了将0从main函数中返回出去 return %18 : $Int32 // id: %16} // end sil function 'main'
- @main 这里是标示我们当前main.swift的入口函数,SIL中的标示符以 @作为前缀
- %0,%1……在SIL也叫寄存器,这里我们可以明白为我们一样寻常开发的常量,一旦赋值之后就不可以在修改,如果SIL中还要继承利用,那么就不停的累加数字
- alloc_global是创建一个全局变量
- global_addr是拿到全局变量的地点,赋值给%3
- metatype是拿到Teacher的Metaldata赋值给%4
- 接下来就是将__allocating_init()的函数地点赋值给%5
- apply是调用函数,这里是调用%5也就是__allocating_init(),%4是参数,并将返回值给%6
- 然后将%6的值存储到%3,也就是我们刚刚创建的全局变量的地点
- 然后是构建Int并return
3. 这内里有些固定搭配
1.初始化一个引用
//初始化全局变量person alloc_global @main.person : main.Teacher//创建对之前由alloc_global初始化的全局变量地点的引用 %3 = global_addr @main.person : main.Teacher 2.调用一个方法
// 方法__allocating_init()的引用 %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher//调用函数__allocating_init,并传入参数元范例Teacher %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher3.得到swfit的根本范例
//创建字面量6 %12 = integer_literal $Builtin.Int64, 6//天生Int值6,swift中Int是结构体 %13 = struct $Int (%12 : $Builtin.Int64)swiftc命令:
天生可实验文件:swiftc -o main.out main.swift天生抽象语法树的命令(AST):swiftc main.swift -dump-ast天生中央语言(SIL):swiftc main.swift -emit-silLLVM中央表现层(LLVM IR):swiftc main.swift -emit -ir天生汇编语言:swiftc main.swift -emit-assembly |